a-game

2D platformer written from scratch.
git clone git://git.amin.space/a-game.git
Log | Files | Refs | README | LICENSE

commit 68087cc3b1f2e69edbdc710a02f6248d2530c45b
parent 704f18b717c404b6a6cd96ec2290534990236aa1
Author: amin <dev@aminmesbah.com>
Date:   Wed, 24 Apr 2019 06:16:51 +0000

Fix tunneling bug by expanding search range

Here's what appears to have happened: the tile search range around the
player's movement path was too small at low velocities. This resulted in
the player occasionally tunneling through a solid tile not included in
the search range, and therefore not collision tested against.

FossilOrigin-Name: 729eafb439c4f0535e174ec9290eaa8a3d89830c77f73697a17f16feb7d930bd
Diffstat:
Msrc/game.c | 76++++++++++++++++++++++++++++++++++------------------------------------------
1 file changed, 34 insertions(+), 42 deletions(-)

diff --git a/src/game.c b/src/game.c @@ -14,11 +14,8 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer) struct GameState *game_state = game_memory->game_state; // init player - //game_state->player.pos = (v2) {12.5f, 5.0f}; - game_state->player.pos = (v2) {10.812491f, 3.752474f}; - game_state->player.velocity = (v2) {0.0f, 5.0f}; - game_state->player.pos = (v2) {10.736823f, 3.591690f}; - game_state->player.velocity = (v2) {5.0f, 5.0f}; + game_state->player.pos = (v2) {12.5f, 5.0f}; + // In Knytt, the player is 9 by 14 texels and a tile is 24 by 24 texels. // These dimensions are relative to a square 'meter', one tile game_state->player.dimensions = (v2) {0.375f, 0.583f}; @@ -272,8 +269,6 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga { color = (v3) {0.8f, 0.4f, 0.4f}; printf("!!!!!!!!!!!!!!!!\n"); - glmth_print(glmth_v2_a(player.pos, glmth_v2f_m(player.dimensions, 0.5f))); - glmth_print(player.velocity); assert(false); } } @@ -295,8 +290,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga f32 dt = game_input->dt; f32 max_meters_per_second = 5.0f; f32 acceleration_rate = 50.0f; - //f32 friction = 0.7f; - f32 friction = 1.0f; + f32 friction = 0.7f; if (game_input->key_up) { @@ -381,13 +375,12 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga #else { #define RENDER_COLLISION_DEBUG_QUAD(r, c) render_debug_quad(game_state, (r), (c), &view, &projection); - printf("--------------\n"); - v2 old_p = player->pos; - v2 new_p = glmth_v2_a(old_p, glmth_v2f_m(player->velocity, dt)); + v2 player_delta = glmth_v2f_m(player->velocity, dt); + v2 new_p = glmth_v2_a(player->pos, player_delta); rect player_traversal_bb = { - .min = {glmth_min(old_p.x, new_p.x), glmth_min(old_p.y, new_p.y)}, - .max = {glmth_max(old_p.x, new_p.x), glmth_max(old_p.y, new_p.y)}, + .min = {glmth_min(player->pos.x, new_p.x), glmth_min(player->pos.y, new_p.y)}, + .max = {glmth_max(player->pos.x, new_p.x), glmth_max(player->pos.y, new_p.y)}, }; //RENDER_COLLISION_DEBUG_QUAD(player_traversal_bb, ((v3) {0.4f, 0.4f, 0.4f})); @@ -396,24 +389,19 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga rect tile_search_range = { .min = { - (i32)glmth_max(0.0f, player_traversal_occupancy_bb.min.x), - (i32)glmth_max(0.0f, player_traversal_occupancy_bb.min.y), + (i32)glmth_max(0.0f, player_traversal_occupancy_bb.min.x - 1), + (i32)glmth_max(0.0f, player_traversal_occupancy_bb.min.y - 1), }, .max = { - (i32)glmth_min(ROOM_TILE_DIM_X, player_traversal_occupancy_bb.max.x + 1), - (i32)glmth_min(ROOM_TILE_DIM_Y, player_traversal_occupancy_bb.max.y + 1), + (i32)glmth_min(ROOM_TILE_DIM_X, player_traversal_occupancy_bb.max.x + 2), + (i32)glmth_min(ROOM_TILE_DIM_Y, player_traversal_occupancy_bb.max.y + 2), }, }; RENDER_COLLISION_DEBUG_QUAD(tile_search_range, ((v3) {0.8f, 0.8f, 0.8f})); - v2 player_delta = glmth_v2_s(new_p, old_p); f32 remaining_time_factor = 1.0f; for (u32 i = 0; i < 4 && remaining_time_factor > 0.0f; i++) { - printf("Iteration %u:\n", i); - old_p = player->pos; - new_p = glmth_v2_a(old_p, glmth_v2f_m(player->velocity, dt)); - player_delta = glmth_v2_s(new_p, old_p); v2 wall_normal = {0}; f32 smallest_distance_scale_factor = 1.0f; for (i32 tile_y = tile_search_range.min.y; tile_y < tile_search_range.max.y; tile_y++) @@ -446,8 +434,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga segment tile_l = {tile_sw, tile_nw}; segment tile_r = {tile_se, tile_ne}; - - struct WallCollision bottom = get_wall_collision(old_p, new_p, tile_b); + struct WallCollision bottom = get_wall_collision(player->pos, new_p, tile_b); if (bottom.collision_occurred) { if (smallest_distance_scale_factor > bottom.distance_scale_factor) @@ -462,7 +449,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {1.0f, 0.0f, 0.0f})); } - struct WallCollision top = get_wall_collision(old_p, new_p, tile_t); + struct WallCollision top = get_wall_collision(player->pos, new_p, tile_t); if (top.collision_occurred) { if (smallest_distance_scale_factor > top.distance_scale_factor) @@ -477,7 +464,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {0.0f, 1.0f, 0.0f})); } - struct WallCollision left = get_wall_collision(old_p, new_p, tile_l); + struct WallCollision left = get_wall_collision(player->pos, new_p, tile_l); if (left.collision_occurred) { if (smallest_distance_scale_factor > left.distance_scale_factor) @@ -492,7 +479,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {0.0f, 0.0f, 1.0f})); } - struct WallCollision right = get_wall_collision(old_p, new_p, tile_r); + struct WallCollision right = get_wall_collision(player->pos, new_p, tile_r); if (right.collision_occurred) { if (smallest_distance_scale_factor > right.distance_scale_factor) @@ -509,21 +496,26 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga } } } - v2 nearest_collision_p = glmth_v2_a(old_p, glmth_v2f_m(player_delta, smallest_distance_scale_factor)); + + v2 delta_to_nearest_collision = glmth_v2f_m(player_delta, smallest_distance_scale_factor); + v2 nearest_collision_p = glmth_v2_a(player->pos, delta_to_nearest_collision); + player->pos = nearest_collision_p; - player->velocity = glmth_v2_s( - player->velocity, - glmth_v2f_m(wall_normal, glmth_v2_dot(player->velocity, wall_normal)) - ); - player_delta = glmth_v2_s( - player_delta, - glmth_v2f_m(player_delta, glmth_v2_dot(player->velocity, wall_normal)) - ); - remaining_time_factor -= smallest_distance_scale_factor * remaining_time_factor; - glmth_print(player_delta); - glmth_print(glmth_v2_a(player->pos, glmth_v2f_m(player->dimensions, 0.5f))); - glmth_print(player->velocity); - printf("time remaining: %f\n", remaining_time_factor); + + // NOTE(amin): + // n * dot(v, n) + // = n * (|v| * |n| * cos(theta)) + // = n * (|v| * 1 * cos(theta)) + // = n * (|v| * cos(theta)) + // = n * |v_projected_onto_n| + // = v_projected_onto_n + v2 colliding_velocity_component = glmth_v2f_m(wall_normal, glmth_v2_dot(player->velocity, wall_normal)); + + player->velocity = glmth_v2_s(player->velocity, colliding_velocity_component); + player_delta = glmth_v2f_m(player->velocity, dt); + new_p = glmth_v2_a(player->pos, player_delta); + + remaining_time_factor -= smallest_distance_scale_factor; } } #undef RENDER_COLLISION_DEBUG_QUAD