Improve collision system

Changelog:
- Add handling for complete overlap
    - It will attempt to move into an empty space near the checked
      entity. If no free space, it will move up. Ripe for exploit!
- Move boundary collision check into movement update
- AABB check now returns overlap mode:
    - 0: no overlap
    - 1: partial overlap
    - 2: complete overlap
scene_man
En Yi 2023-06-05 21:56:57 +08:00
parent dbf80508ec
commit 57cb3ef07a
3 changed files with 99 additions and 76 deletions

View File

@ -1,55 +1,29 @@
#include "AABB.h"
bool find_1D_overlap(Vector2 l1, Vector2 l2, float* overlap)
uint8_t find_1D_overlap(Vector2 l1, Vector2 l2, float* overlap)
{
// No Overlap
if (l1.y < l2.x || l2.y < l1.x) return false;
if (l1.y < l2.x || l2.y < l1.x) return 0;
if (l1.x >= l2.x && l1.y <= l2.y)
{
// Complete Overlap, not sure what to do tbh
//*overlap = l2.y-l2.x + l1.y-l1.x;
float l1_mag = l1.y - l2.x;
float l2_mag = l2.y - l2.x;
float c1 = l2.y - l1.y;
float c2 = l1.x - l2.x;
float len_to_add = l1_mag;
if (l1_mag > l2_mag)
{
len_to_add = l2_mag;
c1 *= -1;
c2 *= -1;
}
if (c1 < c2)
{
*overlap = -len_to_add - c1;
}
else
{
*overlap = len_to_add + c2;
}
// Complete Overlap, any direction is possible
// Cannot give a singular value, but return something anyways
*overlap = l2.y-l2.x + l1.y-l1.x;
return 2;
}
else
{
//Partial overlap
// x is p1, y is p2
*overlap = (l2.y >= l1.y)? l2.x - l1.y : l2.y - l1.x;
}
//if (fabs(*overlap) < 0.01) // Use 2 dp precision
//{
// *overlap = 0;
// return false;
//}
return true;
//Partial overlap
// x is p1, y is p2
*overlap = (l2.y >= l1.y)? l2.x - l1.y : l2.y - l1.x;
return 1;
}
bool find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector2 tl2, const Vector2 sz2, Vector2* overlap)
uint8_t find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector2 tl2, const Vector2 sz2, Vector2* overlap)
{
// Note that we include one extra pixel for checking
// This avoid overlapping on the border
Vector2 l1, l2;
bool overlap_x, overlap_y;
uint8_t overlap_x, overlap_y;
l1.x = tl1.x;
l1.y = tl1.x + sz1.x;
l2.x = tl2.x;
@ -62,7 +36,8 @@ bool find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector2 tl2,
l2.y = tl2.y + sz2.y;
overlap_y = find_1D_overlap(l1, l2, &overlap->y);
return overlap_x && overlap_y;
if (overlap_x == 2 && overlap_y == 2) return 2;
return (overlap_x < overlap_y) ? overlap_x : overlap_y;
}
bool point_in_AABB(Vector2 point, Rectangle box)

View File

@ -2,7 +2,8 @@
#define __AABB_H
#include "raylib.h"
#include "raymath.h"
bool find_1D_overlap(Vector2 l1, Vector2 l2, float* overlap);
bool find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector2 tl2, const Vector2 sz2, Vector2* overlap);
#include <stdint.h>
uint8_t find_1D_overlap(Vector2 l1, Vector2 l2, float* overlap);
uint8_t find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector2 tl2, const Vector2 sz2, Vector2* overlap);
bool point_in_AABB(Vector2 point, Rectangle box);
#endif // __AABB_H

View File

@ -42,6 +42,7 @@ typedef struct CollideEntity {
} CollideEntity_t;
// ------------------------- Collision functions ------------------------------------
// Do not subtract one for the size for any collision check, just pass normally. The extra one is important for AABB test
static bool check_collision(const CollideEntity_t* ent, TileGrid_t* grid, bool check_oneway)
{
for(unsigned int tile_y = ent->area.tile_y1; tile_y <= ent->area.tile_y2; tile_y++)
@ -92,7 +93,6 @@ static bool check_collision(const CollideEntity_t* ent, TileGrid_t* grid, bool c
return false;
}
// TODO: This should be a point collision check, not an AABB check
static bool check_collision_at(
Entity_t* p_ent, Vector2 pos, Vector2 bbox_sz,
@ -146,7 +146,8 @@ static bool check_collision_and_move(
CBBox_t* p_bbox = get_component(ent, CBBOX_COMP_T);
Vector2 overlap = {0,0};
Vector2 prev_overlap = {0,0};
if (find_AABB_overlap(p_ct->position, p_bbox->size, *other_pos, other_bbox, &overlap))
uint8_t overlap_mode = find_AABB_overlap(p_ct->position, p_bbox->size, *other_pos, other_bbox, &overlap);
if (overlap_mode == 1)
{
// If there is collision, use previous overlap to determine direction
find_AABB_overlap(p_ct->prev_position, p_bbox->size, *other_pos, other_bbox, &prev_overlap);
@ -191,9 +192,49 @@ static bool check_collision_and_move(
//*other_pos = Vector2Subtract(*other_pos, offset);
}
}
return true;
}
return false;
else if (overlap_mode == 2)
{
if ( other_solid != SOLID ) goto collision_end;
// On complete overlap, find a free space in this order: top, left, right, bottom
Vector2 point_to_test = {0};
point_to_test.x = p_ct->position.x;
point_to_test.y = other_pos->y - p_bbox->size.y;
if (!check_collision_at(ent, point_to_test, p_bbox->size, tilemap, (Vector2){0}))
{
p_ct->position = point_to_test;
goto collision_end;
}
point_to_test.x = other_pos->x - p_bbox->size.x;
point_to_test.y = p_ct->position.y;
if (!check_collision_at(ent, point_to_test, p_bbox->size, tilemap, (Vector2){0}))
{
p_ct->position = point_to_test;
goto collision_end;
}
point_to_test.x = other_pos->x + other_bbox.x;
point_to_test.y = p_ct->position.y;
if (!check_collision_at(ent, point_to_test, p_bbox->size, tilemap, (Vector2){0}))
{
p_ct->position = point_to_test;
goto collision_end;
}
point_to_test.x = p_ct->position.x;
point_to_test.y = other_pos->y + other_bbox.y;
if (!check_collision_at(ent, point_to_test, p_bbox->size, tilemap, (Vector2){0}))
{
p_ct->position = point_to_test;
goto collision_end;
}
// If no free space, Move up no matter what
p_ct->position.x = p_ct->position.x;
p_ct->position.y = other_pos->y - p_bbox->size.y;
}
collision_end:
return overlap_mode > 0;
}
static uint8_t check_bbox_edges(
@ -676,36 +717,6 @@ void tile_collision_system(Scene_t* scene)
if (p_ctransform->velocity.y > 0) p_ctransform->velocity.y = 0;
}
// Level boundary collision
unsigned int level_width = tilemap.width * TILE_SIZE;
if(p_ctransform->position.x < 0 || p_ctransform->position.x + p_bbox->size.x > level_width)
{
p_ctransform->position.x = (p_ctransform->position.x < 0) ? 0 : p_ctransform->position.x;
if (p_ctransform->position.x + p_bbox->size.x > level_width)
{
p_ctransform->position.x = level_width - p_bbox->size.x;
}
else
{
p_ctransform->position.x = p_ctransform->position.x;
}
p_ctransform->velocity.x *= -1;
}
unsigned int level_height = tilemap.height * TILE_SIZE;
if(p_ctransform->position.y < 0 || p_ctransform->position.y + p_bbox->size.y > level_height)
{
p_ctransform->position.y = (p_ctransform->position.y < 0) ? 0 : p_ctransform->position.y;
if (p_ctransform->position.y + p_bbox->size.y > level_height)
{
p_ctransform->position.y = level_height - p_bbox->size.y;
}
else
{
p_ctransform->position.y = p_ctransform->position.y;
}
p_ctransform->velocity.y *= -1;
}
// Deal with float precision, by rounding when it is near to an integer enough by 2 dp
float decimal;
float fractional = modff(p_ctransform->position.x, &decimal);
@ -1052,10 +1063,13 @@ void player_pushing_system(Scene_t* scene)
void movement_update_system(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
TileGrid_t tilemap = data->tilemap;
// Update movement
float delta_time = 0.017; // TODO: Will need to think about delta time handling
CTransform_t * p_ctransform;
sc_map_foreach_value(&scene->ent_manager.component_map[CTRANSFORM_COMP_T], p_ctransform)
unsigned long ent_idx;
sc_map_foreach(&scene->ent_manager.component_map[CTRANSFORM_COMP_T], ent_idx, p_ctransform)
{
p_ctransform->prev_velocity = p_ctransform->velocity;
p_ctransform->velocity =
@ -1082,6 +1096,39 @@ void movement_update_system(Scene_t* scene)
);
p_ctransform->accel.x = 0;
p_ctransform->accel.y = 0;
// Level boundary collision
Entity_t* p_ent = get_entity(&scene->ent_manager, ent_idx);
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
unsigned int level_width = tilemap.width * TILE_SIZE;
if(p_ctransform->position.x < 0 || p_ctransform->position.x + p_bbox->size.x > level_width)
{
p_ctransform->position.x = (p_ctransform->position.x < 0) ? 0 : p_ctransform->position.x;
if (p_ctransform->position.x + p_bbox->size.x > level_width)
{
p_ctransform->position.x = level_width - p_bbox->size.x;
}
else
{
p_ctransform->position.x = p_ctransform->position.x;
}
p_ctransform->velocity.x = 0;
}
unsigned int level_height = tilemap.height * TILE_SIZE;
if(p_ctransform->position.y < 0 || p_ctransform->position.y + p_bbox->size.y > level_height)
{
p_ctransform->position.y = (p_ctransform->position.y < 0) ? 0 : p_ctransform->position.y;
if (p_ctransform->position.y + p_bbox->size.y > level_height)
{
p_ctransform->position.y = level_height - p_bbox->size.y;
}
else
{
p_ctransform->position.y = p_ctransform->position.y;
}
p_ctransform->velocity.y = 0;
}
}
}