From 5b3cbd1bbaed1aed3b090e6aa5eecd680df4115a Mon Sep 17 00:00:00 2001 From: En Yi Date: Wed, 19 Jul 2023 19:16:35 +0800 Subject: [PATCH] Implement BFS into water runner --- scenes/engine/EC/EC.h | 3 ++ scenes/engine/collisions.h | 1 + scenes/water_flow.c | 76 ++++++++++++++++++++++++++++++++++++-- water_test.c | 10 +++++ 4 files changed, 87 insertions(+), 3 deletions(-) diff --git a/scenes/engine/EC/EC.h b/scenes/engine/EC/EC.h index 67b6372..a6cdb83 100644 --- a/scenes/engine/EC/EC.h +++ b/scenes/engine/EC/EC.h @@ -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; diff --git a/scenes/engine/collisions.h b/scenes/engine/collisions.h index 462305e..52bb20c 100644 --- a/scenes/engine/collisions.h +++ b/scenes/engine/collisions.h @@ -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; diff --git a/scenes/water_flow.c b/scenes/water_flow.c index af61be4..249c883 100644 --- a/scenes/water_flow.c +++ b/scenes/water_flow.c @@ -1,6 +1,6 @@ #include "water_flow.h" - - +#include "sc/queue/sc_queue.h" +#include 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) +{ } diff --git a/water_test.c b/water_test.c index b3f9cb6..264e26f 100644 --- a/water_test.c +++ b/water_test.c @@ -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);