fp: Add basic collision detection

This commit is contained in:
doyle 2023-09-17 21:55:59 +10:00
parent bda508d291
commit 0eca065c6e
2 changed files with 59 additions and 11 deletions

View File

@ -406,6 +406,8 @@ void FP_GameUpdate(TELY_Platform *platform, FP_Game *game, TELY_Renderer *render
} }
} }
// NOTE: Stalk entity ======================================================================
#if 0
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
if (entity->name == DQN_STRING8("Enemy")) { if (entity->name == DQN_STRING8("Enemy")) {
if (entity->handle != game->clicked_entity && entity->stalk_entity != game->clicked_entity) { if (entity->handle != game->clicked_entity && entity->stalk_entity != game->clicked_entity) {
@ -463,6 +465,7 @@ void FP_GameUpdate(TELY_Platform *platform, FP_Game *game, TELY_Renderer *render
entity->local_pos += entity_to_target_pos_norm * (entity->local_hit_box_size.x * .05f); entity->local_pos += entity_to_target_pos_norm * (entity->local_hit_box_size.x * .05f);
} }
} }
#endif
// NOTE: Core equations of motion ========================================================== // NOTE: Core equations of motion ==========================================================
{ {
@ -472,23 +475,69 @@ void FP_GameUpdate(TELY_Platform *platform, FP_Game *game, TELY_Renderer *render
Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s); Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s);
Dqn_f32 t_squared = DQN_SQUARED(t); Dqn_f32 t_squared = DQN_SQUARED(t);
entity->velocity = (acceleration * t) + entity->velocity * 0.82f; entity->velocity = (acceleration * t) + entity->velocity * 0.82f;
Dqn_V2 delta_pos = (acceleration * 0.5f * t_squared) + (entity->velocity * t);
Dqn_V2 delta_p = (acceleration * 0.5f * t_squared) + (entity->velocity * t); Dqn_V2 entity_new_pos = entity->local_pos + delta_pos;
entity->local_pos += delta_p; Dqn_Rect entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
bool has_collision = false;
Dqn_Rect world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
Dqn_Rect new_world_hit_box = world_hit_box;
new_world_hit_box.pos += delta_p;
for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) { for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) {
FP_GameEntity *collider = collider_it.entity; FP_GameEntity *collider = collider_it.entity;
if (collider->handle == entity->handle) if (collider->handle == entity->handle)
continue; continue;
// TODO(doyle): Minkowski sweep? // NOTE: Sweep collider with half the radius of the source entity
Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle); Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle);
if (Dqn_Rect_Intersects(new_world_hit_box, collider_world_hit_box)) { collider_world_hit_box.pos -= (entity_world_hit_box.size * .5f);
collider_world_hit_box.size += entity_world_hit_box.size;
if (!Dqn_Rect_ContainsPoint(collider_world_hit_box, entity_new_pos))
continue;
// NOTE: Solve collision by determining the 't' value at which
// we hit one of the walls of the collider and move the entity
// at exactly that point.
// O + td = x
// td = x - O
// t = (x - O) / d
Dqn_f32 collider_left_wall_x = collider_world_hit_box.pos.x;
Dqn_f32 collider_right_wall_x = collider_world_hit_box.pos.x + collider_world_hit_box.size.w;
Dqn_f32 collider_top_wall_y = collider_world_hit_box.pos.y;
Dqn_f32 collider_bottom_wall_y = collider_world_hit_box.pos.y + collider_world_hit_box.size.h;
Dqn_V2 o = entity_world_hit_box.pos;
Dqn_V2 d = delta_pos;
Dqn_f32 earliest_t = 999.f;
if (d.x != 0.f) {
Dqn_f32 left_t = (collider_left_wall_x - o.x / d.x);
Dqn_f32 right_t = (collider_right_wall_x - o.x / d.x);
if (left_t >= 0.f)
earliest_t = DQN_MIN(earliest_t, left_t);
if (right_t >= 0.f)
earliest_t = DQN_MIN(earliest_t, right_t);
} }
if (d.y != 0.f) {
Dqn_f32 top_t = (collider_top_wall_y - o.y / d.y);
Dqn_f32 bottom_t = (collider_bottom_wall_y - o.y / d.y);
if (top_t >= 0.f)
earliest_t = DQN_MIN(earliest_t, top_t);
if (bottom_t >= 0.f)
earliest_t = DQN_MIN(earliest_t, bottom_t);
}
Dqn_V2 pos_just_before_collide = entity_world_hit_box.pos + (d * t);
Dqn_V2 new_delta_p = pos_just_before_collide - entity_world_hit_box.pos;
entity->local_pos += new_delta_p;
entity->velocity = {};
has_collision = true;
}
if (!has_collision) {
entity->local_pos += delta_pos;
} }
} }
@ -707,10 +756,9 @@ void FP_GameRender(FP_Game *game, TELY_Platform *platform, TELY_Renderer *render
} else if (game->hot_entity == entity->handle || (entity->flags & FP_EntityFlag_DrawHitBox)) { } else if (game->hot_entity == entity->handle || (entity->flags & FP_EntityFlag_DrawHitBox)) {
Dqn_V4 hot_colour = game->hot_entity == entity->handle ? TELY_COLOUR_RED_TOMATO_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_YELLOW_SANDY_V4, .5f); Dqn_V4 hot_colour = game->hot_entity == entity->handle ? TELY_COLOUR_RED_TOMATO_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_YELLOW_SANDY_V4, .5f);
TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, hot_colour); TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, hot_colour);
} }
if (game->clicked_entity == entity->handle || game->hot_entity == entity->handle) { if (game->hot_entity == entity->handle) {
if (entity->name.size) { if (entity->name.size) {
Dqn_V2I player_tile = Dqn_V2I_InitNx2(world_pos.x / FP_TILE_SIZE, world_pos.y / FP_TILE_SIZE); Dqn_V2I player_tile = Dqn_V2I_InitNx2(world_pos.x / FP_TILE_SIZE, world_pos.y / FP_TILE_SIZE);
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);

View File

@ -355,7 +355,7 @@ static Dqn_Rect FP_Game_CalcEntityLocalHitBox(FP_Game const *game, FP_GameEntity
static Dqn_Rect FP_Game_CalcEntityWorldHitBox(FP_Game const *game, FP_GameEntityHandle handle) static Dqn_Rect FP_Game_CalcEntityWorldHitBox(FP_Game const *game, FP_GameEntityHandle handle)
{ {
FP_GameEntity *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle); FP_GameEntity *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, handle); Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, handle);
Dqn_Rect local_hit_box = FP_Game_CalcEntityLocalHitBox(game, entity->handle); Dqn_Rect local_hit_box = FP_Game_CalcEntityLocalHitBox(game, entity->handle);
Dqn_Rect result = Dqn_Rect_InitV2x2(world_pos + local_hit_box.pos, local_hit_box.size); Dqn_Rect result = Dqn_Rect_InitV2x2(world_pos + local_hit_box.pos, local_hit_box.size);