commit 27d37834dd6757196353842ae94ace93866ee1ac
parent 37f3791a33110ddee4973098008ec3765f9c9389
Author: amin <dev@aminmesbah.com>
Date: Tue, 23 Apr 2019 20:00:17 +0000
Try positioning player properly after collision
This still has bugs. You can get inside a tile in some frames.
FossilOrigin-Name: 73f17ca1d4375ddf1ccd6e6534c4a90327034c19cd0b94cebdf6f710abfa1a81
Diffstat:
M | src/game.c | | | 221 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
M | src/glmth.h | | | 8 | ++++++++ |
2 files changed, 122 insertions(+), 107 deletions(-)
diff --git a/src/game.c b/src/game.c
@@ -15,8 +15,6 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer)
// init player
game_state->player.pos = (v2) {12.5f, 5.0f};
- // TODO: remove test velocity
- game_state->player.velocity = (v2) {290.0f, 80.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
@@ -79,8 +77,7 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer)
struct WallCollision
{
bool collision_occurred;
- f32 game_time_offset;
- v2 collision_point;
+ f32 distance_scale_factor;
};
internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_p_final, segment wall)
@@ -108,18 +105,18 @@ internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_
if (player_delta.E[wall_normal_axis] != 0.0f)
{
f32 wall_position = wall.min.E[wall_normal_axis];
- f32 initial_d = wall.min.E[wall_normal_axis] - entity_p_initial.E[wall_normal_axis];
+ f32 initial_d = wall_position - entity_p_initial.E[wall_normal_axis];
- f32 scale_factor = initial_d / player_delta.E[wall_normal_axis];
- v2 collision_point = glmth_v2_a(entity_p_initial, glmth_v2f_m(player_delta, scale_factor));
+ f32 segment_scale_factor = initial_d / player_delta.E[wall_normal_axis];
+ v2 collision_point = glmth_v2_a(entity_p_initial, glmth_v2f_m(player_delta, segment_scale_factor));
- if (scale_factor > 0.0f && scale_factor < 1.0f)
+ if (segment_scale_factor >= 0.0f && segment_scale_factor <= 1.0f)
{
if (collision_point.E[wall_axis] >= wall.min.E[wall_axis]
&& collision_point.E[wall_axis] <= wall.max.E[wall_axis])
{
result.collision_occurred = true;
- result.collision_point = collision_point;
+ result.distance_scale_factor = segment_scale_factor - 0.0001f;
}
}
}
@@ -287,11 +284,9 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
{
struct Entity *player = &game_state->player;
f32 dt = game_input->dt;
- //f32 max_meters_per_second = 5.0f;
+ f32 max_meters_per_second = 5.0f;
f32 acceleration_rate = 50.0f;
- // TODO: refriction
- //f32 friction = 0.7f;
- f32 friction = 1.0f;
+ f32 friction = 0.7f;
if (game_input->key_up)
{
@@ -323,9 +318,8 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
// Semi implicit Euler integration: https://gafferongames.com/post/integration_basics/
player->velocity = glmth_v2_a(player->velocity, glmth_v2f_m(player->acceleration, dt));
- // TODO: reclamp
- //glmth_clamp(&player->velocity.x, -max_meters_per_second, max_meters_per_second);
- //glmth_clamp(&player->velocity.y, -max_meters_per_second, max_meters_per_second);
+ glmth_clamp(&player->velocity.x, -max_meters_per_second, max_meters_per_second);
+ glmth_clamp(&player->velocity.y, -max_meters_per_second, max_meters_per_second);
#if 0
{
@@ -376,6 +370,7 @@ 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);
v2 old_p = player->pos;
v2 new_p = glmth_v2_a(old_p, glmth_v2f_m(player->velocity, dt));
@@ -383,10 +378,10 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
.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)},
};
- //render_debug_quad(game_state, player_traversal_bb, (v3) {0.4f, 0.4f, 0.4f}, &view, &projection);
+ //RENDER_COLLISION_DEBUG_QUAD(player_traversal_bb, ((v3) {0.4f, 0.4f, 0.4f}));
rect player_traversal_occupancy_bb = glmth_minkowski_sum_rect_rect(player_traversal_bb, player->dimensions);
- //render_debug_quad(game_state, player_traversal_occupancy_bb, (v3) {0.8f, 0.4f, 0.8f}, &view, &projection);
+ //RENDER_COLLISION_DEBUG_QUAD(player_traversal_occupancy_bb, ((v3) {0.8f, 0.4f, 0.8f}));
rect tile_search_range = {
.min = {
@@ -398,109 +393,121 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
(i32)glmth_min(ROOM_TILE_DIM_Y, player_traversal_occupancy_bb.max.y + 1),
},
};
- render_debug_quad(game_state, tile_search_range, (v3) {0.8f, 0.8f, 0.8f}, &view, &projection);
+ RENDER_COLLISION_DEBUG_QUAD(tile_search_range, ((v3) {0.8f, 0.8f, 0.8f}));
- for (i32 tile_y = tile_search_range.min.y; tile_y < tile_search_range.max.y; tile_y++)
+ 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++)
{
- for (i32 tile_x = tile_search_range.min.x; tile_x < tile_search_range.max.x; tile_x++)
+ 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++)
{
- assert(tile_x < ROOM_TILE_DIM_X);
- assert(tile_y < ROOM_TILE_DIM_Y);
-
- v2u tile_index = {tile_x, ROOM_TILE_DIM_Y - tile_y - 1};
- u32 tile_id = tiles[tile_index.y][tile_index.x];
-
- if (tile_id == 1)
+ for (i32 tile_x = tile_search_range.min.x; tile_x < tile_search_range.max.x; tile_x++)
{
- rect tile_aabb = {
- .min = {tile_x, tile_y},
- .max = {tile_x + tile_size, tile_y + tile_size},
- };
-
- rect tile_player_sum = glmth_minkowski_sum_rect_rect(tile_aabb, player->dimensions);
- render_debug_quad(game_state, tile_player_sum, (v3) {0.8f, 0.4f, 0.4f}, &view, &projection);
+ assert(tile_x < ROOM_TILE_DIM_X);
+ assert(tile_y < ROOM_TILE_DIM_Y);
- v2 tile_nw = {tile_player_sum.min.x, tile_player_sum.max.y};
- v2 tile_ne = tile_player_sum.max;
- v2 tile_sw = tile_player_sum.min;
- v2 tile_se = {tile_player_sum.max.x, tile_player_sum.min.y};
+ v2u tile_index = {tile_x, ROOM_TILE_DIM_Y - tile_y - 1};
+ u32 tile_id = tiles[tile_index.y][tile_index.x];
- segment tile_b = {tile_sw, tile_se};
- segment tile_t = {tile_nw, tile_ne};
- segment tile_l = {tile_sw, tile_nw};
- segment tile_r = {tile_se, tile_ne};
-
- v2 wall_normal = {0};
-
- struct WallCollision bottom = get_wall_collision(old_p, new_p, tile_b);
- if (bottom.collision_occurred)
+ if (tile_id == 1)
{
- wall_normal = (v2) {0.0f, -1.0f};
- rect collision = {
- .min = tile_aabb.min,
- .max = {tile_aabb.max.x, tile_aabb.min.y + 0.2f},
+ rect tile_aabb = {
+ .min = {tile_x, tile_y},
+ .max = {tile_x + tile_size, tile_y + tile_size},
};
- render_debug_quad(game_state, collision, (v3) {1.0f, 0.0f, 0.0f}, &view, &projection);
- }
-
- struct WallCollision top = get_wall_collision(old_p, new_p, tile_t);
- if (top.collision_occurred)
- {
- wall_normal = (v2) {0.0f, 1.0f};
- rect collision = {
- .min = {tile_aabb.min.x, tile_aabb.max.y - 0.2f},
- .max = tile_aabb.max,
- };
- render_debug_quad(game_state, collision, (v3) {0.0f, 1.0f, 0.0f}, &view, &projection);
- }
-
- struct WallCollision left = get_wall_collision(old_p, new_p, tile_l);
- if (left.collision_occurred)
- {
- wall_normal = (v2) {-1.0f, 0.0f};
- rect collision = {
- .min = tile_aabb.min,
- .max = {tile_aabb.min.x + 0.2f, tile_aabb.max.y},
- };
- render_debug_quad(game_state, collision, (v3) {0.0f, 0.0f, 1.0f}, &view, &projection);
- }
- struct WallCollision right = get_wall_collision(old_p, new_p, tile_r);
- if (right.collision_occurred)
- {
- wall_normal = (v2) {1.0f, 0.0f};
- rect collision = {
- .min = {tile_aabb.max.x - 0.2f, tile_aabb.min.y},
- .max = tile_aabb.max,
- };
- render_debug_quad(game_state, collision, (v3) {1.0f, 0.0f, 1.0f}, &view, &projection);
+ rect tile_player_sum = glmth_minkowski_sum_rect_rect(tile_aabb, player->dimensions);
+ RENDER_COLLISION_DEBUG_QUAD(tile_player_sum, ((v3) {0.8f, 0.4f, 0.4f}));
+
+ v2 tile_nw = {tile_player_sum.min.x, tile_player_sum.max.y};
+ v2 tile_ne = tile_player_sum.max;
+ v2 tile_sw = tile_player_sum.min;
+ v2 tile_se = {tile_player_sum.max.x, tile_player_sum.min.y};
+
+ segment tile_b = {tile_sw, tile_se};
+ segment tile_t = {tile_nw, tile_ne};
+ 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);
+ if (bottom.collision_occurred)
+ {
+ if (smallest_distance_scale_factor > bottom.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = bottom.distance_scale_factor;
+ }
+ wall_normal = (v2) {0.0f, -1.0f};
+ rect collision = {
+ .min = tile_aabb.min,
+ .max = {tile_aabb.max.x, tile_aabb.min.y + 0.2f},
+ };
+ RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {1.0f, 0.0f, 0.0f}));
+ }
+
+ struct WallCollision top = get_wall_collision(old_p, new_p, tile_t);
+ if (top.collision_occurred)
+ {
+ if (smallest_distance_scale_factor > top.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = top.distance_scale_factor;
+ }
+ wall_normal = (v2) {0.0f, 1.0f};
+ rect collision = {
+ .min = {tile_aabb.min.x, tile_aabb.max.y - 0.2f},
+ .max = tile_aabb.max,
+ };
+ RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {0.0f, 1.0f, 0.0f}));
+ }
+
+ struct WallCollision left = get_wall_collision(old_p, new_p, tile_l);
+ if (left.collision_occurred)
+ {
+ if (smallest_distance_scale_factor > left.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = left.distance_scale_factor;
+ }
+ wall_normal = (v2) {-1.0f, 0.0f};
+ rect collision = {
+ .min = tile_aabb.min,
+ .max = {tile_aabb.min.x + 0.2f, tile_aabb.max.y},
+ };
+ RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {0.0f, 0.0f, 1.0f}));
+ }
+
+ struct WallCollision right = get_wall_collision(old_p, new_p, tile_r);
+ if (right.collision_occurred)
+ {
+ if (smallest_distance_scale_factor > right.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = right.distance_scale_factor;
+ }
+ wall_normal = (v2) {1.0f, 0.0f};
+ rect collision = {
+ .min = {tile_aabb.max.x - 0.2f, tile_aabb.min.y},
+ .max = tile_aabb.max,
+ };
+ RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {1.0f, 0.0f, 1.0f}));
+ }
}
}
}
- }
-
- //player->pos = new_p;
- // render new player
- {
- shader_use(&game_state->player.shader);
- glBindVertexArray(game_state->tiles.vao);
- struct Entity player = game_state->player;
-
- m4 model = glmth_m4_init_id();
- model = glmth_translate(model, (v3) {new_p.x, new_p.y, 0.0f});
- model = glmth_scale(model, (v3) {player.dimensions.x, player.dimensions.y, 1.0f});
-
- v3 color = (v3) { 1.0f, 0.0f, 1.0f };
-
- shader_setv3(&game_state->tiles.shader, "color", &color);
- shader_setm4(&game_state->tiles.shader, "model", &model);
- shader_setm4(&game_state->tiles.shader, "view", &view);
- shader_setm4(&game_state->tiles.shader, "projection", &projection);
-
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
- glBindVertexArray(0);
+ v2 nearest_collision_p = glmth_v2_a(old_p, glmth_v2f_m(player_delta, smallest_distance_scale_factor));
+ 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;
}
}
+#undef RENDER_COLLISION_DEBUG_QUAD
#endif
glmth_clamp(&player->pos.x, 0.0f, ROOM_TILE_DIM_X);
glmth_clamp(&player->pos.y, 0.0f, ROOM_TILE_DIM_Y);
diff --git a/src/glmth.h b/src/glmth.h
@@ -175,8 +175,16 @@ internal inline v2 glmth_v2_normalize(v2 v)
return r;
}
+internal inline f32 glmth_v2_dot(v2 vec1, v2 vec2)
+{
+ // dot product, a.k.a. inner product or scalar product
+ f32 dot_product = (vec1.x * vec2.x) + (vec1.y * vec2.y);
+ return dot_product;
+}
+
internal inline v3 glmth_v3_cross(v3 vec1, v3 vec2)
{
+ // cross product, a.k.a. outer product or vector product
v3 r = {
.x = (vec1.y * vec2.z) - (vec1.z * vec2.y),
.y = (vec1.z * vec2.x) - (vec1.x * vec2.z),