From 68586cd1d280d9d9e1ba88298ad47cb561a82c97 Mon Sep 17 00:00:00 2001 From: En Yi Date: Wed, 21 Dec 2022 23:40:10 +0800 Subject: [PATCH] Add crouching to player Changelog: - Add player state component to keep track of crouching - Add crouch key handling - Crouch mechanics: - Reduce vertical bbox, slightly stretch horizontally - Offset position to be on ground - Reverse for un-crouching - Can jump out of a crouch, reverting the bbox - Dont allow un-crouch if there is something overhead (maybe should check directly overhead, instead of a full overhead?) - Above also apply for jump out of crouch --- components.h | 17 +++++++++- mempool.c | 6 ++-- scene_impl.c | 94 ++++++++++++++++++++++++++++++++++++++++++++-------- scene_impl.h | 2 +- scene_test.c | 1 + 5 files changed, 103 insertions(+), 17 deletions(-) diff --git a/components.h b/components.h index faf28b2..8bc972c 100644 --- a/components.h +++ b/components.h @@ -3,19 +3,22 @@ #include "raylib.h" // TODO: Look at sc to use macros to auto generate functions -#define N_COMPONENTS 4 +#define N_COMPONENTS 5 enum ComponentEnum { CBBOX_COMP_T, CTRANSFORM_COMP_T, CTILECOORD_COMP_T, CJUMP_COMP_T, + CPLAYERSTATE_T }; typedef enum ComponentEnum ComponentEnum_t; typedef struct _CBBox_t { Vector2 size; + Vector2 offset; + Vector2 half_size; }CBBox_t; typedef struct _CTransform_t @@ -44,4 +47,16 @@ typedef struct _CJump_t bool jumped; bool short_hop; }CJump_t; + +typedef enum PlayerState +{ + GROUNDED, + AIR, +}PlayerState_t; + +typedef struct _CPlayerState_t +{ + unsigned int is_crouch: 1; + unsigned int in_water:1; +}CPlayerState_t; #endif // __COMPONENTS_H diff --git a/mempool.c b/mempool.c index 885437b..6bf2259 100644 --- a/mempool.c +++ b/mempool.c @@ -7,7 +7,8 @@ static Entity_t entity_buffer[MAX_COMP_POOL_SIZE]; static CBBox_t bbox_buffer[MAX_COMP_POOL_SIZE]; static CTransform_t ctransform_buffer[MAX_COMP_POOL_SIZE]; static CTileCoord_t ctilecoord_buffer[MAX_COMP_POOL_SIZE]; -static CJump_t cjump_buffer[MAX_COMP_POOL_SIZE]; +static CJump_t cjump_buffer[1]; // Only player is expected to have this +static CPlayerState_t cplayerstate_buffer[1]; // Only player is expected to have this // Use hashmap as a Set // Use list will be used to check if an object exist @@ -30,7 +31,8 @@ static MemPool_t comp_mempools[N_COMPONENTS] = {bbox_buffer, MAX_COMP_POOL_SIZE, sizeof(CBBox_t), {0}, {0}}, {ctransform_buffer, MAX_COMP_POOL_SIZE, sizeof(CTransform_t), {0}, {0}}, {ctilecoord_buffer, MAX_COMP_POOL_SIZE, sizeof(CTileCoord_t), {0}, {0}}, - {cjump_buffer, MAX_COMP_POOL_SIZE, sizeof(CJump_t), {0}, {0}}, + {cjump_buffer, 1, sizeof(CJump_t), {0}, {0}}, + {cplayerstate_buffer, 1, sizeof(CPlayerState_t), {0}, {0}}, }; static MemPool_t ent_mempool = {entity_buffer, MAX_COMP_POOL_SIZE, sizeof(Entity_t), {0}, {0}}; diff --git a/scene_impl.c b/scene_impl.c index ae30487..fd63bdb 100644 --- a/scene_impl.c +++ b/scene_impl.c @@ -55,9 +55,27 @@ static bool find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector return overlap_x && overlap_y; } -static bool check_on_ground(Vector2 pos, Vector2 bbox_sz, TileGrid_t* grid) +static bool check_collision_at(Vector2 pos, Vector2 bbox_sz, TileGrid_t* grid, Vector2 point) { - unsigned int tile_x1 = pos.x / TILE_SIZE; + Vector2 new_pos = Vector2Add(pos, point); + unsigned int tile_x1 = (new_pos.x) / TILE_SIZE; + unsigned int tile_x2 = (new_pos.x + bbox_sz.x - 1) / TILE_SIZE; + unsigned int tile_y1 = (new_pos.y) / TILE_SIZE; + unsigned int tile_y2 = (new_pos.y + bbox_sz.y - 1) / TILE_SIZE; + + for(unsigned int tile_y = tile_y1; tile_y <= tile_y2; tile_y++) + { + for(unsigned int tile_x = tile_x1; tile_x <= tile_x2; tile_x++) + { + if (grid->tiles[tile_y*grid->width + tile_x].solid) return true; + } + } + return false; +} + +static inline bool check_on_ground(Vector2 pos, Vector2 bbox_sz, TileGrid_t* grid) +{ + unsigned int tile_x1 = (pos.x) / TILE_SIZE; unsigned int tile_x2 = (pos.x + bbox_sz.x - 1) / TILE_SIZE; unsigned int tile_y = (pos.y + bbox_sz.y) / TILE_SIZE; @@ -114,17 +132,21 @@ static void level_scene_render_func(Scene_t* scene) { CTransform_t* p_ct = get_component(&scene->ent_manager, p_ent, CTRANSFORM_COMP_T); CJump_t* p_cjump = get_component(&scene->ent_manager, p_ent, CJUMP_COMP_T); + CPlayerState_t* p_pstate = get_component(&scene->ent_manager, p_ent, CPLAYERSTATE_T); char buffer[64]; sprintf(buffer, "Pos: %.3f\n %.3f", p_ct->position.x, p_ct->position.y); DrawText(buffer, tilemap.width * TILE_SIZE + 1, 15, 12, BLACK); sprintf(buffer, "Jumps: %u", p_cjump->jumps); DrawText(buffer, tilemap.width * TILE_SIZE + 1, 60, 12, BLACK); + sprintf(buffer, "Crouch: %s", p_pstate->is_crouch? "YES":"NO"); + DrawText(buffer, tilemap.width * TILE_SIZE + 1, 90, 12, BLACK); } } static void player_movement_input_system(Scene_t* scene) { LevelSceneData_t *data = (LevelSceneData_t *)scene->scene_data; + TileGrid_t tilemap = data->tilemap; // Deal with player acceleration/velocity via inputs first float mag = Vector2Length(data->player_dir); @@ -134,13 +156,12 @@ static void player_movement_input_system(Scene_t* scene) { CTransform_t* p_ctransform = get_component(&scene->ent_manager, p_player, CTRANSFORM_COMP_T); CJump_t* p_cjump = get_component(&scene->ent_manager, p_player, CJUMP_COMP_T); + CBBox_t* p_bbox = get_component(&scene->ent_manager, p_player, CBBOX_COMP_T); + CPlayerState_t* p_pstate = get_component(&scene->ent_manager, p_player, CPLAYERSTATE_T); p_ctransform->accel.x = 0; p_ctransform->accel.y = 0; p_ctransform->accel = Vector2Scale(Vector2Normalize(data->player_dir), 2000); - CBBox_t* p_bbox = get_component(&scene->ent_manager, p_player, CBBOX_COMP_T); - bool on_ground = check_on_ground(p_ctransform->position, p_bbox->size, &data->tilemap); - p_ctransform->on_ground = on_ground; if (p_cjump->jumped && p_ctransform->velocity.y < 0) { if (!data->jumped_pressed && !p_cjump->short_hop) @@ -149,20 +170,66 @@ static void player_movement_input_system(Scene_t* scene) p_ctransform->velocity.y /= 2; } } - if (!on_ground) + if (!p_ctransform->on_ground) { p_ctransform->accel = Vector2Add(p_ctransform->accel, GRAVITY); + if(p_pstate->is_crouch) + { + p_pstate->is_crouch = false; + p_ctransform->position.y -= 23; + p_ctransform->position.x += 5; + p_bbox->size.y = 45; + p_bbox->size.x = 30; + } } else { if (data->jumped_pressed && p_cjump->jumps > 0) { - p_ctransform->velocity.y -= p_cjump->jump_speed; - p_cjump->jumped = true; - p_cjump->jumps--; + bool jump_valid = true; + if (p_pstate->is_crouch) + { + Vector2 test_pos = p_ctransform->position; + Vector2 test_bbox = {30, 45}; + Vector2 top = {0, 0}; + test_pos.y -= 23; + test_pos.x += 5; + jump_valid = !check_collision_at(test_pos, test_bbox, &tilemap, top); + } + + if (jump_valid) + { + p_ctransform->velocity.y -= p_cjump->jump_speed; + p_cjump->jumped = true; + p_cjump->jumps--; + } + } + + if (data->crouch_pressed && !p_pstate->is_crouch) + { + p_ctransform->position.y += 23; + p_ctransform->position.x -= 5; + p_bbox->size.y = 22; + p_bbox->size.x = 40; + p_pstate->is_crouch = data->crouch_pressed; + } + else if (!data->crouch_pressed && p_pstate->is_crouch) + { + Vector2 test_pos = p_ctransform->position; + Vector2 test_bbox = {30, 45}; + Vector2 top = {0, 0}; + test_pos.y -= 23; + test_pos.x += 5; + if (!check_collision_at(test_pos, test_bbox, &tilemap, top)) + { + p_ctransform->position.y -= 23; + p_ctransform->position.x += 5; + p_bbox->size.y = 45; + p_bbox->size.x = 30; + p_pstate->is_crouch = data->crouch_pressed; + } } } - } data->player_dir.x = 0; data->player_dir.y = 0; @@ -210,7 +277,6 @@ static void player_ground_air_transition_system(Scene_t* scene) CBBox_t* p_bbox = get_component(&scene->ent_manager, p_player, CBBOX_COMP_T); CJump_t* p_cjump = get_component(&scene->ent_manager, p_player, CJUMP_COMP_T); bool on_ground = check_on_ground(p_ctransform->position, p_bbox->size, &data->tilemap); - // Handle Ground<->Air Transition if (!on_ground && p_ctransform->on_ground) { @@ -222,6 +288,7 @@ static void player_ground_air_transition_system(Scene_t* scene) p_cjump->jumped = false; p_cjump->short_hop = false; } + p_ctransform->on_ground = on_ground; } } @@ -404,7 +471,7 @@ void level_do_action(Scene_t *scene, ActionType_t action, bool pressed) data->jumped_pressed = pressed; break; case ACTION_DOWN: - // data->player_dir.y = 1; + data->crouch_pressed = pressed; break; case ACTION_LEFT: data->player_dir.x = (pressed)? -1 : 0; @@ -421,14 +488,15 @@ void init_level_scene(LevelScene_t *scene) init_scene(&scene->scene, LEVEL_SCENE, &level_scene_render_func, &level_do_action); scene->scene.scene_data = &scene->data; scene->data.jumped_pressed = false; + scene->data.crouch_pressed = false; memset(&scene->data.player_dir, 0, sizeof(Vector2)); // insert level scene systems sc_array_add(&scene->scene.systems, &player_movement_input_system); sc_array_add(&scene->scene.systems, &movement_update_system); - sc_array_add(&scene->scene.systems, &player_ground_air_transition_system); sc_array_add(&scene->scene.systems, &update_tilemap_system); sc_array_add(&scene->scene.systems, &player_collision_system); + sc_array_add(&scene->scene.systems, &player_ground_air_transition_system); sc_array_add(&scene->scene.systems, &toggle_block_system); // This avoid graphical glitch, not essential diff --git a/scene_impl.h b/scene_impl.h index ed548bc..2efc322 100644 --- a/scene_impl.h +++ b/scene_impl.h @@ -24,7 +24,7 @@ typedef struct LevelSceneData Vector2 player_dir; TileGrid_t tilemap; bool jumped_pressed; - bool jumped_released; + bool crouch_pressed; }LevelSceneData_t; typedef struct LevelScene diff --git a/scene_test.c b/scene_test.c index efa70ab..2895da3 100644 --- a/scene_test.c +++ b/scene_test.c @@ -25,6 +25,7 @@ int main(void) p_cjump->jump_speed = 680; p_cjump->jumps = 1; p_cjump->max_jumps = 1; + add_component(&scene.scene.ent_manager, p_ent, CPLAYERSTATE_T); update_entity_manager(&scene.scene.ent_manager); //for (size_t step = 0; step < 6000; step++) while(true)