Implement BFS into water runner

scene_man
En Yi 2023-07-19 19:16:35 +08:00
parent 917fdeba9b
commit 5b3cbd1bba
4 changed files with 87 additions and 3 deletions

View File

@ -143,6 +143,9 @@ typedef struct _BFSTileMap {
typedef struct _CWaterRunner {
int32_t current_tile;
int32_t target_tile;
struct sc_queue_32 bfs_queue;
bool* visited;
BFSTileMap_t bfs_tilemap;
}CWaterRunner_t;

View File

@ -15,6 +15,7 @@ typedef struct Tile {
SolidType_t solid;
uint8_t def;
unsigned int water_level;
unsigned int max_water_level;
struct sc_map_64v entities_set;
Vector2 offset;
Vector2 size;

View File

@ -1,6 +1,6 @@
#include "water_flow.h"
#include "sc/queue/sc_queue.h"
#include <stdio.h>
Entity_t* create_water_runner(EntityManager_t* ent_manager, int32_t width, int32_t height, int32_t start_tile)
{
Entity_t* p_filler = add_entity(ent_manager, DYNMEM_ENT_TAG);
@ -22,7 +22,11 @@ Entity_t* create_water_runner(EntityManager_t* ent_manager, int32_t width, int32
p_crunner->bfs_tilemap.height = height;
p_crunner->bfs_tilemap.len = total;
sc_queue_init(&p_crunner->bfs_queue);
p_crunner->visited = calloc(total, sizeof(bool));
p_crunner->current_tile = start_tile;
p_crunner->target_tile = total;
CTransform_t* p_ct = add_component(p_filler, CTRANSFORM_COMP_T);
p_ct->movement_mode = KINEMATIC_MOVEMENT;
@ -34,10 +38,11 @@ void free_water_runner(Entity_t* ent, EntityManager_t* ent_manager)
{
CWaterRunner_t* p_crunner = get_component(ent, CWATERRUNNER_T);
free(p_crunner->bfs_tilemap.tilemap);
free(p_crunner->visited);
sc_queue_term(&p_crunner->bfs_queue);
remove_entity(ent_manager, ent->m_id);
}
void update_water_runner_system(Scene_t* scene)
{
// The core of the water runner is to:
@ -58,4 +63,69 @@ void update_water_runner_system(Scene_t* scene)
// - No need to recheck already reachable tiles
// - If current tile is solid, scan left and right for reachable tile and start from there
// Repeat scanline fill
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
TileGrid_t tilemap = data->tilemap;
CWaterRunner_t* p_crunner;
unsigned int ent_idx;
sc_map_foreach(&scene->ent_manager.component_map[CWATERRUNNER_T], ent_idx, p_crunner)
{
//Entity_t* ent = get_entity(&scene->ent_manager, ent_idx);
sc_queue_add_last(&p_crunner->bfs_queue, p_crunner->current_tile);
memset(p_crunner->visited, 0, p_crunner->bfs_tilemap.len * sizeof(bool));
int32_t lowest_tile = p_crunner->current_tile;
while (!sc_queue_empty(&p_crunner->bfs_queue))
{
unsigned int curr_idx = sc_queue_peek_first(&p_crunner->bfs_queue);
sc_queue_del_first(&p_crunner->bfs_queue);
if (p_crunner->visited[curr_idx]) continue;
unsigned int curr_height = curr_idx / p_crunner->bfs_tilemap.width;
unsigned int curr_low = lowest_tile / p_crunner->bfs_tilemap.width;
if (curr_height > curr_low)
{
lowest_tile = curr_idx;
}
p_crunner->visited[curr_idx] = true;
p_crunner->bfs_tilemap.tilemap[curr_idx].reachable = true;
// Possible optimisation to avoid repeated BFS, dunno how possible
unsigned int next = curr_idx + p_crunner->bfs_tilemap.width;
if (next >= p_crunner->bfs_tilemap.len) continue;
Tile_t* next_tile = tilemap.tiles + next;
if (next_tile->solid != SOLID && next_tile->water_level < next_tile->max_water_level)
{
sc_queue_add_last(&p_crunner->bfs_queue, next);
}
else
{
next = curr_idx - 1;
if (next % p_crunner->bfs_tilemap.width != 0)
{
next_tile = tilemap.tiles + next;
if (next_tile->solid != SOLID && next_tile->water_level < next_tile->max_water_level)
{
sc_queue_add_last(&p_crunner->bfs_queue, next);
}
}
next = curr_idx + 1;
if (next % p_crunner->bfs_tilemap.width != 0)
{
next_tile = tilemap.tiles + next;
if (next_tile->solid != SOLID && next_tile->water_level < next_tile->max_water_level)
{
sc_queue_add_last(&p_crunner->bfs_queue, next);
}
}
}
}
p_crunner->target_tile = lowest_tile;
}
}
void init_water_runner_system(void)
{
}

View File

@ -85,6 +85,12 @@ static void level_scene_render_func(Scene_t* scene)
unsigned int x = ((p_runner->current_tile) % tilemap.width) * tilemap.tile_size;
unsigned int y = ((p_runner->current_tile) / tilemap.width) * tilemap.tile_size;
DrawCircle(x+16, y+16, 8, ColorAlpha(BLACK, 0.2));
if (p_runner->target_tile < p_runner->bfs_tilemap.len)
{
unsigned int x = ((p_runner->target_tile) % tilemap.width) * tilemap.tile_size;
unsigned int y = ((p_runner->target_tile) / tilemap.width) * tilemap.tile_size;
DrawCircle(x+16, y+16, 8, ColorAlpha(BLUE, 0.2));
}
}
char buffer[64] = {0};
@ -309,6 +315,7 @@ int main(void)
all_tiles[i].solid = NOT_SOLID;
all_tiles[i].tile_type = EMPTY_TILE;
all_tiles[i].moveable = true;
all_tiles[i].max_water_level = 1;
sc_map_init_64v(&all_tiles[i].entities_set, 16, 0);
all_tiles[i].size = (Vector2){TILE_SIZE, TILE_SIZE};
}
@ -330,9 +337,12 @@ int main(void)
sc_array_add(&scene.scene.systems, &simple_friction_system);
sc_array_add(&scene.scene.systems, &movement_update_system);
sc_array_add(&scene.scene.systems, &update_tilemap_system);
sc_array_add(&scene.scene.systems, &update_water_runner_system);
sc_array_add(&scene.scene.systems, &toggle_block_system);
sc_array_add(&scene.scene.systems, &camera_update_system);
sc_array_add(&scene.scene.systems, &player_dir_reset_system);
sc_map_put_64(&scene.scene.action_map, KEY_R, ACTION_RESTART);
sc_map_put_64(&scene.scene.action_map, KEY_UP, ACTION_UP);
sc_map_put_64(&scene.scene.action_map, KEY_DOWN, ACTION_DOWN);