commit c79354bf54276bc99002fc404dda161925b127ee
parent 41c2e6ed9f56bf284f097164cf05d708746a9c02
Author: amin <dev@aminmesbah.com>
Date: Thu, 25 Apr 2019 04:54:14 +0000
Ignore internal walls when colliding
Finally! Smooth (and seemingly robust) movement along solid surfaces!!!
FossilOrigin-Name: 9b5c39a203aa578e4c0a21214d21e106e9f9a42b034bcc3d1c31536f3ac20ae0
Diffstat:
M | src/game.c | | | 175 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | src/glmth.h | | | 37 | +++++++++++++++++++++++++++++++++++++ |
2 files changed, 151 insertions(+), 61 deletions(-)
diff --git a/src/game.c b/src/game.c
@@ -2,6 +2,9 @@
#include "glad.c"
#include "shader.c"
+// One tile is one square meter
+#define TILE_SIZE 1.0f
+
#ifdef PLATFORM_HOTLOAD_GAME_CODE
void game_load_opengl_symbols(void)
{
@@ -15,7 +18,6 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer)
// init player
game_state->player.pos = (v2) {12.5f, 5.0f};
- game_state->player.pos = (v2) {12.5f, 8.5f};
// 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
@@ -134,6 +136,55 @@ internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_
return result;
}
+internal bool tile_is_solid(u32 tiles[][ROOM_TILE_DIM_X], v2 tile_pos)
+{
+ assert(tiles);
+
+ bool is_solid = false;
+
+ v2u tile_index = {
+ tile_pos.x,
+ ROOM_TILE_DIM_Y - 1.0f - tile_pos.y,
+ };
+
+ assert(tile_index.x >= 0);
+ assert(tile_index.x < ROOM_TILE_DIM_X);
+ assert(tile_index.y >= 0);
+ assert(tile_index.y < ROOM_TILE_DIM_X);
+
+ u32 tile_value = tiles[tile_index.y][tile_index.x];
+ is_solid = tile_value > 0;
+
+ return is_solid;
+}
+
+internal bool wall_is_internal(u32 tiles[][ROOM_TILE_DIM_X], segment wall)
+{
+ bool is_internal = false;
+ bool wall_is_horizontal = wall.min.y == wall.max.y;
+ bool wall_is_vertical = wall.min.x == wall.max.x;
+
+ assert(wall_is_vertical || wall_is_horizontal);
+
+ v2 lesser_neighbor = {0};
+ v2 greater_neighbor = {0};
+
+ if (wall_is_vertical)
+ {
+ lesser_neighbor = (v2) {wall.min.x - TILE_SIZE, wall.min.y};
+ greater_neighbor = wall.min;
+ is_internal = tile_is_solid(tiles, lesser_neighbor) && tile_is_solid(tiles, greater_neighbor);
+ }
+ else
+ {
+ lesser_neighbor = (v2) {wall.min.x, wall.min.y - TILE_SIZE};
+ greater_neighbor = wall.min;
+ is_internal = tile_is_solid(tiles, lesser_neighbor) && tile_is_solid(tiles, greater_neighbor);
+ }
+
+ return is_internal;
+}
+
// NOTE(amin): For now updating and rendering are interleaved. We simulate the
// world at the same rate we render it. Our timestep is not fixed. We may want
// to change this in the future.
@@ -218,8 +269,6 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
- // One tile is one square meter
- f32 tile_size = 1.0f;
// render tiles
{
@@ -240,9 +289,9 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
// our square verts are anchored around the center point of the
// square, so we want to offset by 0.5 to instead have our
// anchor in the min corner
- model = glmth_translate(model, (v3) {tile_size * 0.5f, tile_size * 0.5f, 0.0f});
+ model = glmth_translate(model, (v3) {TILE_SIZE * 0.5f, TILE_SIZE * 0.5f, 0.0f});
model = glmth_translate(model, (v3) {tile_pos.x, tile_pos.y, 0.0f});
- model = glmth_scale(model, (v3) {tile_size, tile_size, 1.0f});
+ model = glmth_scale(model, (v3) {TILE_SIZE, TILE_SIZE, 1.0f});
v3 color;
@@ -265,7 +314,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
};
rect tile_aabb = {
.min = tile_pos,
- .max = {tile_pos.x + tile_size, tile_pos.y + tile_size},
+ .max = {tile_pos.x + TILE_SIZE, tile_pos.y + TILE_SIZE},
};
bool player_is_in_tile = glmth_intersect_aabb_aabb(player_aabb, tile_aabb);
@@ -276,7 +325,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
if (player_is_in_tile && tile_id > 0)
{
color = (v3) {0.8f, 0.4f, 0.4f};
- //assert(false);
+ assert(false);
}
}
@@ -368,7 +417,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
rect tile_aabb = {
.min = {x, y},
- .max = {x + tile_size, y + tile_size},
+ .max = {x + TILE_SIZE, y + TILE_SIZE},
};
rect player_aabb = {
.min = {next_player_pos.x - half_w, next_player_pos.y - half_h},
@@ -434,85 +483,89 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
{
rect tile_aabb = {
.min = {tile_x, tile_y},
- .max = {tile_x + tile_size, tile_y + tile_size},
+ .max = {tile_x + TILE_SIZE, tile_y + TILE_SIZE},
};
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};
+ segment tile_sum_b = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_BOTTOM);
+ segment tile_sum_t = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_TOP);
+ segment tile_sum_l = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_LEFT);
+ segment tile_sum_r = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_RIGHT);
v2 norm_b = {0.0f, -1.0f};
v2 norm_t = {0.0f, 1.0f};
v2 norm_l = {-1.0f, 0.0f};
v2 norm_r = {1.0f, 0.0f};
- struct WallCollision bottom = get_wall_collision(player->pos, new_p, tile_b, norm_b);
- if (bottom.collision_occurred)
+ if (!wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_BOTTOM)))
{
- if (smallest_distance_scale_factor > bottom.distance_scale_factor)
+ struct WallCollision bottom = get_wall_collision(player->pos, new_p, tile_sum_b, norm_b);
+ if (bottom.collision_occurred)
{
- smallest_distance_scale_factor = bottom.distance_scale_factor;
+ if (smallest_distance_scale_factor > bottom.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = bottom.distance_scale_factor;
+ }
+ wall_normal = norm_b;
+ 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}));
}
- wall_normal = norm_b;
- 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(player->pos, new_p, tile_t, norm_t);
- if (top.collision_occurred)
+ if (!wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_TOP)))
{
- if (smallest_distance_scale_factor > top.distance_scale_factor)
+ struct WallCollision top = get_wall_collision(player->pos, new_p, tile_sum_t, norm_t);
+ if (top.collision_occurred)
{
- smallest_distance_scale_factor = top.distance_scale_factor;
+ if (smallest_distance_scale_factor > top.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = top.distance_scale_factor;
+ }
+ wall_normal = norm_t;
+ 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}));
}
- wall_normal = norm_t;
- 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(player->pos, new_p, tile_l, norm_l);
- if (left.collision_occurred)
+ if (!wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_LEFT)))
{
- if (smallest_distance_scale_factor > left.distance_scale_factor)
+ struct WallCollision left = get_wall_collision(player->pos, new_p, tile_sum_l, norm_l);
+ if (left.collision_occurred)
{
- smallest_distance_scale_factor = left.distance_scale_factor;
+ if (smallest_distance_scale_factor > left.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = left.distance_scale_factor;
+ }
+ wall_normal = norm_l;
+ 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}));
}
- wall_normal = norm_l;
- 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(player->pos, new_p, tile_r, norm_r);
- if (right.collision_occurred)
+ if (!wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_RIGHT)))
{
- if (smallest_distance_scale_factor > right.distance_scale_factor)
+ struct WallCollision right = get_wall_collision(player->pos, new_p, tile_sum_r, norm_r);
+ if (right.collision_occurred)
{
- smallest_distance_scale_factor = right.distance_scale_factor;
+ if (smallest_distance_scale_factor > right.distance_scale_factor)
+ {
+ smallest_distance_scale_factor = right.distance_scale_factor;
+ }
+ wall_normal = norm_r;
+ 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}));
}
- wall_normal = norm_r;
- 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}));
}
}
}
diff --git a/src/glmth.h b/src/glmth.h
@@ -498,6 +498,43 @@ internal inline rect glmth_minkowski_sum_rect_rect(rect r1, v2 r2_dimensions)
return sum;
}
+enum GlmthRectEdge
+{
+ RECT_EDGE_BOTTOM,
+ RECT_EDGE_TOP,
+ RECT_EDGE_LEFT,
+ RECT_EDGE_RIGHT,
+};
+
+internal inline segment glmth_rect_get_edge(rect r, enum GlmthRectEdge e)
+{
+ v2 nw = {r.min.x, r.max.y};
+ v2 ne = r.max;
+ v2 sw = r.min;
+ v2 se = {r.max.x, r.min.y};
+
+ segment edge = {0};
+ switch(e)
+ {
+ case RECT_EDGE_BOTTOM:
+ edge = (segment) {sw, se};
+ break;
+ case RECT_EDGE_TOP:
+ edge = (segment) {nw, ne};
+ break;
+ case RECT_EDGE_LEFT:
+ edge = (segment) {sw, nw};
+ break;
+ case RECT_EDGE_RIGHT:
+ edge = (segment) {se, ne};
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ return edge;
+}
+
internal inline void glmth_v2_print(v2 v)
{
printf("( %f, %f )\n", v.x, v.y);