improve collision stability
This commit is contained in:
parent
c3a4a06403
commit
2230bab37a
176
src/game.c
176
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 <raylib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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 };
|
||||
|
||||
@ -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);
|
||||
|
||||
11
src/utils.c
11
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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user