a-game

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

commit c7a57149e4dd3faee96fa81f92e7d23a9a181630
parent c9251221fc319bf126f25bf9b706b943a76ad6a2
Author: amin <dev@aminmesbah.com>
Date:   Tue, 30 Apr 2019 04:01:58 +0000

Split some code out into new files

FossilOrigin-Name: afc6d004125b330cca89179671fd1990838767e358bff799b88783b57438d209
Diffstat:
Asrc/collision.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/game.c | 219++++---------------------------------------------------------------------------
Msrc/game.h | 20+-------------------
Asrc/world.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/world.h | 18++++++++++++++++++
5 files changed, 227 insertions(+), 229 deletions(-)

diff --git a/src/collision.c b/src/collision.c @@ -0,0 +1,61 @@ +struct WallCollision +{ + bool collision_occurred; + f32 distance_scale_factor; +}; + +internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_p_final, segment wall, v2 wall_normal) + +{ + struct WallCollision result = {0}; + result.collision_occurred = false; + + assert(math_v2_le(wall.min, wall.max)); + + bool wall_is_horizontal = wall.min.y == wall.max.y; + bool wall_is_vertical = wall.min.x == wall.max.x; + assert(wall_is_horizontal || wall_is_vertical); + + enum VectorAxis wall_axis = AXIS_X; + enum VectorAxis wall_normal_axis = AXIS_Y; + if (wall_is_vertical) + { + wall_axis = AXIS_Y; + wall_normal_axis = AXIS_X; + } + + v2 player_delta = math_v2_s(entity_p_final, entity_p_initial); + + // NOTE(amin): We needn't normalize player_delta here because its magnitude + // becomes irrelevant as the value of cos(theta) nears zero, and we are + // concerned only about the behavior near pi/2 and 3pi/2, where cos(theta) + // is zero. Therefore we simply treat player_delta as if it _were_ + // normalized. + f32 cos_theta = math_v2_dot(player_delta, wall_normal); + + bool theta_is_obtuse = cos_theta < 0.0f; + if (theta_is_obtuse) + { + f32 wall_position = wall.min.E[wall_normal_axis]; + + // Not to be confused with the anime about street racing + f32 initial_d = wall_position - entity_p_initial.E[wall_normal_axis]; + + f32 segment_scale_factor = initial_d / player_delta.E[wall_normal_axis]; + v2 collision_point = math_v2_a(entity_p_initial, math_v2f_m(player_delta, segment_scale_factor)); + + if (math_in_interval_open(segment_scale_factor, 0.0f, 1.0f)) + { + if (math_in_interval_open( + collision_point.E[wall_axis], + wall.min.E[wall_axis], + wall.max.E[wall_axis])) + { + result.collision_occurred = true; + result.distance_scale_factor = math_max(0.0f, segment_scale_factor); + } + } + } + + return result; +} diff --git a/src/game.c b/src/game.c @@ -2,13 +2,8 @@ #include "glad.c" #include "shader.c" #include "memory.c" - -#ifdef PLATFORM_HOTLOAD_GAME_CODE -void game_load_opengl_symbols(void) -{ - gladLoadGL(); -} -#endif +#include "collision.c" +#include "world.c" internal void game_init(struct GameMemory *game_memory, v2u framebuffer) { @@ -30,10 +25,8 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer) game_memory->buffer + sizeof(struct GameState), game_memory->buffer_size - sizeof(struct GameState)); - game_state->world = mem_st_alloc_struct(&game_state->world_allocator, struct World); - struct Room knytt_hanging_fly_village = { .index = {0, 0}, .tiles = { @@ -134,207 +127,6 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer) } } -internal struct AbsolutePos pos_recanonicalize(struct AbsolutePos p) -{ - struct AbsolutePos p_final = p; - v2i room_index_delta = {0}; - - if (p.local.x < 0.0f) - { - room_index_delta.x -= 1; - p_final.local.x = math_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); - } - else if (p.local.x > ROOM_TILE_DIM_X) - { - room_index_delta.x += 1; - p_final.local.x = math_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); - } - - if (p.local.y < 0.0f) - { - room_index_delta.y -= 1; - p_final.local.y = math_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); - } - else if (p.local.y > ROOM_TILE_DIM_Y) - { - room_index_delta.y += 1; - p_final.local.y = math_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); - } - - p_final.room = math_v2i_a(p.room, room_index_delta); - return p_final; -} - -struct RoomSearchResult -{ - bool room_found; - size_t room_array_index; -}; - -internal struct RoomSearchResult get_room_at_index(struct Room *rooms, u32 num_rooms, v2i room_i) -{ - struct RoomSearchResult result = {0}; - for (size_t i = 0; i < num_rooms; i++) - { - struct Room *r = &rooms[i]; - if (r->index.x == room_i.x && r->index.y == room_i.y) - { - result.room_found = true; - result.room_array_index = i; - } - } - return result; -} - -struct WallCollision -{ - bool collision_occurred; - f32 distance_scale_factor; -}; - -internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_p_final, segment wall, v2 wall_normal) - -{ - struct WallCollision result = {0}; - result.collision_occurred = false; - - assert(math_v2_le(wall.min, wall.max)); - - bool wall_is_horizontal = wall.min.y == wall.max.y; - bool wall_is_vertical = wall.min.x == wall.max.x; - assert(wall_is_horizontal || wall_is_vertical); - - enum VectorAxis wall_axis = AXIS_X; - enum VectorAxis wall_normal_axis = AXIS_Y; - if (wall_is_vertical) - { - wall_axis = AXIS_Y; - wall_normal_axis = AXIS_X; - } - - v2 player_delta = math_v2_s(entity_p_final, entity_p_initial); - - // NOTE(amin): We needn't normalize player_delta here because its magnitude - // becomes irrelevant as the value of cos(theta) nears zero, and we are - // concerned only about the behavior near pi/2 and 3pi/2, where cos(theta) - // is zero. Therefore we simply treat player_delta as if it _were_ - // normalized. - f32 cos_theta = math_v2_dot(player_delta, wall_normal); - - bool theta_is_obtuse = cos_theta < 0.0f; - if (theta_is_obtuse) - { - f32 wall_position = wall.min.E[wall_normal_axis]; - - // Not to be confused with the anime about street racing - f32 initial_d = wall_position - entity_p_initial.E[wall_normal_axis]; - - f32 segment_scale_factor = initial_d / player_delta.E[wall_normal_axis]; - v2 collision_point = math_v2_a(entity_p_initial, math_v2f_m(player_delta, segment_scale_factor)); - - if (math_in_interval_open(segment_scale_factor, 0.0f, 1.0f)) - { - if (math_in_interval_open( - collision_point.E[wall_axis], - wall.min.E[wall_axis], - wall.max.E[wall_axis])) - { - result.collision_occurred = true; - result.distance_scale_factor = math_max(0.0f, segment_scale_factor); - } - } - } - - return result; -} - -internal u32 get_tile_value(u32 *tiles, v2 tile_pos) -{ - assert(tiles); - - v2u tile_i_2d = { - tile_pos.x, - ROOM_TILE_DIM_Y - 1.0f - tile_pos.y, - }; - - assert(math_v2_lt( - (v2) {tile_i_2d.x, tile_i_2d.y}, - (v2) {ROOM_TILE_DIM_X, ROOM_TILE_DIM_Y})); - - assert(math_v2_ge( - (v2) {tile_i_2d.x, tile_i_2d.y}, - (v2) {0})); - - u32 tile_i_linearized = (ROOM_TILE_DIM_X * tile_i_2d.y) + tile_i_2d.x; - assert(tile_i_linearized < ROOM_TILE_COUNT); - - u32 tile_value = tiles[tile_i_linearized]; - return tile_value; -} - -internal bool tile_is_solid(u32 *tiles, v2 tile_pos) -{ - assert(tiles); - - bool is_solid = false; - u32 tile_value = get_tile_value(tiles, tile_pos); - is_solid = tile_value > 0; - return is_solid; -} - -internal bool wall_is_screen_edge(u32 *tiles, segment wall) -{ - assert(tiles); - - bool is_screen_edge = 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); - assert(math_v2_ge(wall.min, (v2) {0})); - assert(math_v2_le(wall.max, (v2) {ROOM_TILE_DIM_X, ROOM_TILE_DIM_Y})); - - if (wall_is_vertical) - { - is_screen_edge = wall.min.x == 0.0f || wall.min.x == ROOM_TILE_DIM_X; - } - else - { - is_screen_edge = wall.min.y == 0.0f || wall.min.y == ROOM_TILE_DIM_Y; - } - - return is_screen_edge; -} - -internal bool wall_is_internal(u32 *tiles, segment wall) -{ - assert(tiles); - - 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. @@ -750,3 +542,10 @@ internal void render_debug_quad(struct GameState *game_state, rect r, v3 color, glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, initial_ebo); } + +#ifdef PLATFORM_HOTLOAD_GAME_CODE +void game_load_opengl_symbols(void) +{ + gladLoadGL(); +} +#endif diff --git a/src/game.h b/src/game.h @@ -12,14 +12,11 @@ #include "am_math.h" #include "shader.h" #include "memory.h" +#include "world.h" #include "platform.h" // One tile is one square meter #define TILE_SIZE 1.0f -#define ROOM_TILE_DIM_X 25 -#define ROOM_TILE_DIM_Y 10 -#define ROOM_TILE_COUNT ROOM_TILE_DIM_X * ROOM_TILE_DIM_Y -#define ROOM_ASPECT_RATIO (f32)ROOM_TILE_DIM_X / (f32)ROOM_TILE_DIM_Y struct RendererState { @@ -44,21 +41,6 @@ struct Entity v2 dimensions; }; -struct Room -{ - v2i index; - u32 tiles[ROOM_TILE_COUNT]; -}; - -// TODO: delete -#define MAX_ROOMS 120 - -struct World -{ - struct Room rooms[MAX_ROOMS]; - u32 num_rooms; -}; - struct GameState { struct RendererState renderer; diff --git a/src/world.c b/src/world.c @@ -0,0 +1,138 @@ +internal struct AbsolutePos pos_recanonicalize(struct AbsolutePos p) +{ + struct AbsolutePos p_final = p; + v2i room_index_delta = {0}; + + if (p.local.x < 0.0f) + { + room_index_delta.x -= 1; + p_final.local.x = math_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); + } + else if (p.local.x > ROOM_TILE_DIM_X) + { + room_index_delta.x += 1; + p_final.local.x = math_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); + } + + if (p.local.y < 0.0f) + { + room_index_delta.y -= 1; + p_final.local.y = math_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); + } + else if (p.local.y > ROOM_TILE_DIM_Y) + { + room_index_delta.y += 1; + p_final.local.y = math_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); + } + + p_final.room = math_v2i_a(p.room, room_index_delta); + return p_final; +} + +struct RoomSearchResult +{ + bool room_found; + size_t room_array_index; +}; + +internal struct RoomSearchResult get_room_at_index(struct Room *rooms, u32 num_rooms, v2i room_i) +{ + struct RoomSearchResult result = {0}; + for (size_t i = 0; i < num_rooms; i++) + { + struct Room *r = &rooms[i]; + if (r->index.x == room_i.x && r->index.y == room_i.y) + { + result.room_found = true; + result.room_array_index = i; + } + } + return result; +} + +internal u32 get_tile_value(u32 *tiles, v2 tile_pos) +{ + assert(tiles); + + v2u tile_i_2d = { + tile_pos.x, + ROOM_TILE_DIM_Y - 1.0f - tile_pos.y, + }; + + assert(math_v2_lt( + (v2) {tile_i_2d.x, tile_i_2d.y}, + (v2) {ROOM_TILE_DIM_X, ROOM_TILE_DIM_Y})); + + assert(math_v2_ge( + (v2) {tile_i_2d.x, tile_i_2d.y}, + (v2) {0})); + + u32 tile_i_linearized = (ROOM_TILE_DIM_X * tile_i_2d.y) + tile_i_2d.x; + assert(tile_i_linearized < ROOM_TILE_COUNT); + + u32 tile_value = tiles[tile_i_linearized]; + return tile_value; +} + +internal bool tile_is_solid(u32 *tiles, v2 tile_pos) +{ + assert(tiles); + + bool is_solid = false; + u32 tile_value = get_tile_value(tiles, tile_pos); + is_solid = tile_value > 0; + return is_solid; +} + +internal bool wall_is_screen_edge(u32 *tiles, segment wall) +{ + assert(tiles); + + bool is_screen_edge = 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); + assert(math_v2_ge(wall.min, (v2) {0})); + assert(math_v2_le(wall.max, (v2) {ROOM_TILE_DIM_X, ROOM_TILE_DIM_Y})); + + if (wall_is_vertical) + { + is_screen_edge = wall.min.x == 0.0f || wall.min.x == ROOM_TILE_DIM_X; + } + else + { + is_screen_edge = wall.min.y == 0.0f || wall.min.y == ROOM_TILE_DIM_Y; + } + + return is_screen_edge; +} + +internal bool wall_is_internal(u32 *tiles, segment wall) +{ + assert(tiles); + + 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; +} diff --git a/src/world.h b/src/world.h @@ -0,0 +1,18 @@ +// TODO: delete +#define MAX_ROOMS 120 +#define ROOM_TILE_DIM_X 25 +#define ROOM_TILE_DIM_Y 10 +#define ROOM_TILE_COUNT ROOM_TILE_DIM_X * ROOM_TILE_DIM_Y +#define ROOM_ASPECT_RATIO (f32)ROOM_TILE_DIM_X / (f32)ROOM_TILE_DIM_Y + +struct Room +{ + v2i index; + u32 tiles[ROOM_TILE_COUNT]; +}; + +struct World +{ + struct Room rooms[MAX_ROOMS]; + u32 num_rooms; +};