improve collision stability

This commit is contained in:
Rokas Puzonas 2025-12-07 14:48:36 +02:00
parent c3a4a06403
commit 2230bab37a
3 changed files with 140 additions and 48 deletions

View File

@ -1,10 +1,6 @@
#include "game.h" #include "game.h"
#include "entity.h"
#include "entity_id_list.h" #include "entity_id_list.h"
#include "float_list.h" #include "float_list.h"
#include "raymath.h"
#include <raylib.h>
#include <stdio.h>
void game_init(struct Game *game) 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){ EntityId wall_id = game_add_wall(game, (Rectangle){
.x = 10, .x = 10,
@ -87,6 +90,28 @@ void game_init(struct Game *game)
struct Entity *wall = entity_list_get(&game->entities, wall_id); struct Entity *wall = entity_list_get(&game->entities, wall_id);
wall->velocity.x = -100; 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) void game_free(struct Game *game)
@ -116,6 +141,26 @@ static float game_aabb(Rectangle b1, Rectangle b2)
return CheckCollisionRecs(b1, 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 { struct CollisionResult {
float time; float time;
Vector2 normal; Vector2 normal;
@ -123,59 +168,61 @@ struct CollisionResult {
static bool game_swept_aabb(Rectangle b1, Vector2 v1, Rectangle b2, struct CollisionResult *result) 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) { if (v1.x > 0) {
entryX = b2.x - (b1.x + b1.width); entry_x = rect_get_left(b2) - rect_get_right(b1);
exitX = (b2.x + b2.width) - b1.x; exit_x = rect_get_right(b2) - rect_get_left(b1);
} else { } else {
entryX = (b2.x + b2.width) - b1.x; entry_x = rect_get_right(b2) - rect_get_left(b1);
exitX = b2.x - (b1.x + b1.width); exit_x = rect_get_left(b2) - rect_get_right(b1);
} }
float entryY, exitY; float entry_y, exit_y;
if (v1.y > 0) { if (v1.y > 0) {
entryY = b2.y - (b1.y + b1.height); entry_y = rect_get_top(b2) - rect_get_bottom(b1);
exitY = (b2.y + b2.height) - b1.y; exit_y = rect_get_bottom(b2) - rect_get_top(b1);
} else { } else {
entryY = (b2.y + b2.height) - b1.y; entry_y = rect_get_bottom(b2) - rect_get_top(b1);
exitY = b2.y - (b1.y + b1.height); exit_y = rect_get_top(b2) - rect_get_bottom(b1);
} }
float entryXTime, exitXTime; float entry_x_time, exit_x_time;
if (v1.x == 0) { if (v1.x == 0) {
entryXTime = -INFINITY; entry_x_time = -INFINITY;
exitXTime = INFINITY; exit_x_time = INFINITY;
} else { } else {
entryXTime = entryX / v1.x; entry_x_time = entry_x / v1.x;
exitXTime = exitX / v1.x; exit_x_time = exit_x / v1.x;
} }
float entryYTime, exitYTime; float entry_y_time, exit_y_time;
if (v1.y == 0) { if (v1.y == 0) {
entryYTime = -INFINITY; entry_y_time = -INFINITY;
exitYTime = INFINITY; exit_y_time = INFINITY;
} else { } else {
entryYTime = entryY / v1.y; entry_y_time = entry_y / v1.y;
exitYTime = exitY / v1.y; exit_y_time = exit_y / v1.y;
} }
float entryTime = MAX(entryXTime, entryYTime); float entry_time = MAX(entry_x_time, entry_y_time);
float exitTime = MIN(exitXTime, exitYTime); 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; return false;
} }
ASSERT(0 <= entryTime && entryTime <= 1);
*result = (struct CollisionResult){ *result = (struct CollisionResult){
.time = entryTime, .time = entry_time,
}; };
if (entryXTime > entryYTime) { if (entry_x_time > entry_y_time) {
result->normal.x = entryX < 0.0 ? 1 : -1; result->normal.x = (exit_x - entry_x) < 0.0 ? 1 : -1;
} else { } else {
result->normal.y = entryY < 0.0 ? 1 : -1; result->normal.y = (exit_y - entry_y) < 0.0 ? 1 : -1;
} }
return true; return true;
@ -303,16 +350,6 @@ static void game_collide_against_walls(struct Game *game, struct EntityIdList *w
TracyCZoneEnd(game_collide_against_walls) 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) void game_tick(struct Game *game)
{ {
TracyCZoneN(game_tick, "game_tick", true); 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]); struct Entity *wall = entity_list_get(&game->entities, walls.items[i]);
ASSERT(wall != NULL); ASSERT(wall != NULL);
for (size_t i = 0; i < players.len; i++) { for (size_t j = 0; j < players.len; j++) {
struct Entity *player = entity_list_get(&game->entities, players.items[i]); struct Entity *player = entity_list_get(&game->entities, players.items[j]);
ASSERT(player != NULL); ASSERT(player != NULL);
Rectangle wall_collider = rect_add_position(wall->collision_rect, wall->position); 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.x += wall->step.x * (1 - collision.time);
player->step.y += wall->step.y * (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 &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); 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) { entity_list_foreach(&game->entities, entity) {
if (entity->type == ENTITY_TYPE_PLAYER) { if (entity->type == ENTITY_TYPE_PLAYER) {
Vector2 size = (Vector2){ 20, 20 }; Vector2 size = (Vector2){ 20, 20 };

View File

@ -36,3 +36,4 @@ extern struct Resources g_resources;
Color rgb(uint8_t r, uint8_t g, uint8_t b); Color rgb(uint8_t r, uint8_t g, uint8_t b);
Color hex(const char *str); Color hex(const char *str);
int signf(float value);

View File

@ -37,3 +37,14 @@ Color hex(const char *str)
uint8_t b = parse_hex_byte(&str[5]); uint8_t b = parse_hex_byte(&str[5]);
return rgb(r, g, b); return rgb(r, g, b);
} }
int signf(float value)
{
if (value > 0) {
return 1;
} else if (value < 0) {
return -1;
} else {
return 0;
}
}