From 2230bab37aaea0191cde1a38c73c9c7ed0902df6 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 7 Dec 2025 14:48:36 +0200 Subject: [PATCH] improve collision stability --- src/game.c | 176 ++++++++++++++++++++++++++++++++++++++-------------- src/main.h | 1 + src/utils.c | 11 ++++ 3 files changed, 140 insertions(+), 48 deletions(-) diff --git a/src/game.c b/src/game.c index b27dafc..ac41f68 100644 --- a/src/game.c +++ b/src/game.c @@ -1,10 +1,6 @@ #include "game.h" -#include "entity.h" #include "entity_id_list.h" #include "float_list.h" -#include "raymath.h" -#include -#include void game_init(struct Game *game) { @@ -66,6 +62,13 @@ void game_init(struct Game *game) }); } + game_add_wall(game, (Rectangle){ + .x = 300, + .y = 10, + .width = 40, + .height = 40 + }); + { EntityId wall_id = game_add_wall(game, (Rectangle){ .x = 10, @@ -87,6 +90,28 @@ void game_init(struct Game *game) struct Entity *wall = entity_list_get(&game->entities, wall_id); wall->velocity.x = -100; } + + { + EntityId wall_id = game_add_wall(game, (Rectangle){ + .x = 10, + .y = 00, + .width = 200, + .height = 40 + }); + struct Entity *wall = entity_list_get(&game->entities, wall_id); + wall->velocity.y = 100; + } + + { + EntityId wall_id = game_add_wall(game, (Rectangle){ + .x = 10, + .y = 500, + .width = 200, + .height = 40 + }); + struct Entity *wall = entity_list_get(&game->entities, wall_id); + wall->velocity.y = -100; + } } void game_free(struct Game *game) @@ -116,6 +141,26 @@ static float game_aabb(Rectangle b1, Rectangle b2) return CheckCollisionRecs(b1, b2); } +static float rect_get_left(Rectangle rect) +{ + return rect.x; +} + +static float rect_get_right(Rectangle rect) +{ + return rect.x + rect.width; +} + +static float rect_get_top(Rectangle rect) +{ + return rect.y; +} + +static float rect_get_bottom(Rectangle rect) +{ + return rect.y + rect.height; +} + struct CollisionResult { float time; Vector2 normal; @@ -123,59 +168,61 @@ struct CollisionResult { static bool game_swept_aabb(Rectangle b1, Vector2 v1, Rectangle b2, struct CollisionResult *result) { - float entryX, exitX; + float entry_x, exit_x; if (v1.x > 0) { - entryX = b2.x - (b1.x + b1.width); - exitX = (b2.x + b2.width) - b1.x; + entry_x = rect_get_left(b2) - rect_get_right(b1); + exit_x = rect_get_right(b2) - rect_get_left(b1); } else { - entryX = (b2.x + b2.width) - b1.x; - exitX = b2.x - (b1.x + b1.width); + entry_x = rect_get_right(b2) - rect_get_left(b1); + exit_x = rect_get_left(b2) - rect_get_right(b1); } - float entryY, exitY; + float entry_y, exit_y; if (v1.y > 0) { - entryY = b2.y - (b1.y + b1.height); - exitY = (b2.y + b2.height) - b1.y; + entry_y = rect_get_top(b2) - rect_get_bottom(b1); + exit_y = rect_get_bottom(b2) - rect_get_top(b1); } else { - entryY = (b2.y + b2.height) - b1.y; - exitY = b2.y - (b1.y + b1.height); + entry_y = rect_get_bottom(b2) - rect_get_top(b1); + exit_y = rect_get_top(b2) - rect_get_bottom(b1); } - float entryXTime, exitXTime; + float entry_x_time, exit_x_time; if (v1.x == 0) { - entryXTime = -INFINITY; - exitXTime = INFINITY; + entry_x_time = -INFINITY; + exit_x_time = INFINITY; } else { - entryXTime = entryX / v1.x; - exitXTime = exitX / v1.x; + entry_x_time = entry_x / v1.x; + exit_x_time = exit_x / v1.x; } - float entryYTime, exitYTime; + float entry_y_time, exit_y_time; if (v1.y == 0) { - entryYTime = -INFINITY; - exitYTime = INFINITY; + entry_y_time = -INFINITY; + exit_y_time = INFINITY; } else { - entryYTime = entryY / v1.y; - exitYTime = exitY / v1.y; + entry_y_time = entry_y / v1.y; + exit_y_time = exit_y / v1.y; } - float entryTime = MAX(entryXTime, entryYTime); - float exitTime = MIN(exitXTime, exitYTime); + float entry_time = MAX(entry_x_time, entry_y_time); + float exit_time = MIN(exit_x_time, exit_y_time); - if (entryTime > exitTime || (entryXTime < 0 && entryYTime < 0) || entryXTime > 1.0f || entryYTime > 1.0f) { + if (entry_time > exit_time || + (entry_x_time < 0 && entry_y_time < 0) || + entry_x_time > 1.0f || + entry_y_time > 1.0f + ) { return false; } - ASSERT(0 <= entryTime && entryTime <= 1); - *result = (struct CollisionResult){ - .time = entryTime, + .time = entry_time, }; - if (entryXTime > entryYTime) { - result->normal.x = entryX < 0.0 ? 1 : -1; + if (entry_x_time > entry_y_time) { + result->normal.x = (exit_x - entry_x) < 0.0 ? 1 : -1; } else { - result->normal.y = entryY < 0.0 ? 1 : -1; + result->normal.y = (exit_y - entry_y) < 0.0 ? 1 : -1; } return true; @@ -303,16 +350,6 @@ static void game_collide_against_walls(struct Game *game, struct EntityIdList *w TracyCZoneEnd(game_collide_against_walls) } -static int signf(float value) { - if (value > 0) { - return 1; - } else if (value < 0) { - return -1; - } else { - return 0; - } -} - void game_tick(struct Game *game) { TracyCZoneN(game_tick, "game_tick", true); @@ -369,8 +406,8 @@ void game_tick(struct Game *game) struct Entity *wall = entity_list_get(&game->entities, walls.items[i]); ASSERT(wall != NULL); - for (size_t i = 0; i < players.len; i++) { - struct Entity *player = entity_list_get(&game->entities, players.items[i]); + for (size_t j = 0; j < players.len; j++) { + struct Entity *player = entity_list_get(&game->entities, players.items[j]); ASSERT(player != NULL); Rectangle wall_collider = rect_add_position(wall->collision_rect, wall->position); @@ -391,9 +428,6 @@ void game_tick(struct Game *game) player->step.x += wall->step.x * (1 - collision.time); player->step.y += wall->step.y * (1 - collision.time); - - player->step.x -= collision.normal.x * 0.001; - player->step.y -= collision.normal.y * 0.001; } } @@ -411,9 +445,55 @@ void game_tick(struct Game *game) &player->step ); + for (size_t j = 0; j < walls.len; j++) { + struct Entity *wall = entity_list_get(&game->entities, walls.items[j]); + ASSERT(wall != NULL); + + Rectangle player_collider = rect_add_position(player->collision_rect, Vector2Add(player->position, player->step)); + Rectangle wall_collider = rect_add_position(wall->collision_rect, wall->position); + + if (game_aabb(player_collider, wall_collider)) { + Vector2 player_center = rect_get_center(player_collider); + Vector2 wall_center = rect_get_center(wall_collider); + + float x_overlap = 0; + if (player_center.x < wall_center.x) { + x_overlap = MIN(rect_get_left(wall_collider) - rect_get_right(player_collider), 0); + } else { + x_overlap = MAX(rect_get_right(wall_collider) - rect_get_left(player_collider), 0); + } + + float y_overlap = 0; + if (player_center.y < wall_center.y) { + y_overlap = MIN(rect_get_top(wall_collider) - rect_get_bottom(player_collider), 0); + } else { + y_overlap = MAX(rect_get_bottom(wall_collider) - rect_get_top(player_collider), 0); + } + + float overlap_size = 0; + if (fabs(x_overlap) < fabs(y_overlap)) { + overlap_size = fabs(x_overlap); + player->step.x += x_overlap; + } else { + overlap_size = fabs(y_overlap); + player->step.y += y_overlap; + } + + if (overlap_size > 1) { + // TODO: apply damage to player + } + } + } + player->position = Vector2Add(player->position, player->step); } + for (size_t i = 0; i < walls.len; i++) { + struct Entity *wall = entity_list_get(&game->entities, walls.items[i]); + ASSERT(wall != NULL); + + } + entity_list_foreach(&game->entities, entity) { if (entity->type == ENTITY_TYPE_PLAYER) { Vector2 size = (Vector2){ 20, 20 }; diff --git a/src/main.h b/src/main.h index 1a22a23..eede77d 100644 --- a/src/main.h +++ b/src/main.h @@ -36,3 +36,4 @@ extern struct Resources g_resources; Color rgb(uint8_t r, uint8_t g, uint8_t b); Color hex(const char *str); +int signf(float value); diff --git a/src/utils.c b/src/utils.c index 80952ce..3a03618 100644 --- a/src/utils.c +++ b/src/utils.c @@ -37,3 +37,14 @@ Color hex(const char *str) uint8_t b = parse_hex_byte(&str[5]); return rgb(r, g, b); } + +int signf(float value) +{ + if (value > 0) { + return 1; + } else if (value < 0) { + return -1; + } else { + return 0; + } +}