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 "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 };
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
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]);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user