Compare commits

...

16 Commits

Author SHA1 Message Date
En Yi b5790ef00b Update spike orientation logic
Changelog:
- Remove redundant checks
- Add downwards tile check first
- Fix incorrect rightward tile check
2023-06-23 22:17:59 +08:00
En Yi 491f0bcbef Add extra check in crushing function
Changelog:
- Extra check to avoid just fringing the player bbox edges.
  Crushing requires actual overlap on at least one of the edges.
2023-06-23 22:12:20 +08:00
En Yi 7a670f85fc Extend one pixel outwards for tile collision check 2023-06-23 21:52:06 +08:00
En Yi e79fb249d2 Fix regression in one-way tile edge check 2023-06-23 21:33:40 +08:00
En Yi c7ef3f473f Add spike destruction
Changelog:
- Spike collision is now checked against all bboxes
    - Non player will destroy it
- Add moveable flag for each tile to determine whether a boulder can
  move into it
2023-06-22 23:14:07 +08:00
En Yi 3ee14d5f47 Add bbox check even for solid tile 2023-06-22 22:44:07 +08:00
En Yi 0f5a347cec Ignore fragile bbox when during player crush check 2023-06-22 22:38:22 +08:00
En Yi bb4a9a8f3c Improve crushing response
Changelog:
- Fix incomplete line-AABB collision check
- Add line-AABB check for grid
- Change len_reduction to 0
- Set tiles to check once during edge checking
2023-06-22 22:19:39 +08:00
En Yi 5267c318f2 Give display name when selecting entity to spawn 2023-06-21 23:40:08 +08:00
En Yi 2b878ae784 Add player collision with spikes 2023-06-21 22:32:14 +08:00
En Yi 451b241460 Correct AABB tests
Changelog:
- Figure out why the previous AABB behaviour was correct: continuum
    [0, 5) does not collide with [5, 10)
    For tile check however, need to subtract one to avoid extra tile
    check. Exception is the tile collision
2023-06-21 22:11:32 +08:00
En Yi c8d2ee5408 Improve crushing using edge testing
Changelog:
- Add len reduction to edge checking to give leeway in crushing
2023-06-21 21:27:13 +08:00
En Yi 4b91ab5667 Integrate line check into edge check function
Internal Changelog:
- Weird issue when using the new AABB functions, revert the off-by-one
  fix
2023-06-21 21:13:02 +08:00
En Yi 7767b38221 Add line-AABB collision check function 2023-06-20 22:23:32 +08:00
En Yi c27fa632a2 Add spikes spawning
Changelog:
- Add new tile: spikes
- Change bbox of tile of spikes depending on solid tiles position
2023-06-19 22:32:18 +08:00
En Yi 81da536e8e Add bounding box info in tiles
Changelog:
- Use more memory, but should help out in implementing spikes
2023-06-19 21:08:42 +08:00
7 changed files with 464 additions and 100 deletions

View File

@ -14,12 +14,13 @@ enum EntitySpawnSelection {
TOGGLE_TILE = 0,
TOGGLE_ONEWAY,
TOGGLE_LADDER,
TOGGLE_SPIKE,
SPAWN_CRATE,
SPAWN_METAL_CRATE,
SPAWN_BOULDER,
};
#define MAX_SPAWN_TYPE 6
#define MAX_SPAWN_TYPE 7
static unsigned int current_spawn_selection = 0;
static inline unsigned int get_tile_idx(int x, int y, const TileGrid_t* tilemap)
@ -35,6 +36,21 @@ static inline unsigned int get_tile_idx(int x, int y, const TileGrid_t* tilemap)
return MAX_N_TILES;
}
static char* get_spawn_selection_string(enum EntitySpawnSelection sel)
{
switch(sel)
{
case TOGGLE_TILE: return "solid tile";
case TOGGLE_ONEWAY: return "wooden tile";
case TOGGLE_LADDER: return "ladder";
case TOGGLE_SPIKE: return "spike";
case SPAWN_CRATE: return "wooden crate";
case SPAWN_METAL_CRATE: return "metal crate";
case SPAWN_BOULDER: return "boulder";
default: return "unknown";
}
}
static void level_scene_render_func(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
@ -68,6 +84,13 @@ static void level_scene_render_func(Scene_t* scene)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, ORANGE);
}
else if (tilemap.tiles[i].tile_type == SPIKES)
{
DrawRectangle(
x + tilemap.tiles[i].offset.x, y + tilemap.tiles[i].offset.y,
tilemap.tiles[i].size.x, tilemap.tiles[i].size.y, RED
);
}
if (tilemap.tiles[i].water_level > 0)
{
@ -75,6 +98,12 @@ static void level_scene_render_func(Scene_t* scene)
Color water_colour = ColorAlpha(BLUE, 0.5);
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, water_colour);
}
if (tilemap.tiles[i].moveable)
{
// Draw water tile
Color water_colour = ColorAlpha(GREEN, 0.2);
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, water_colour);
}
}
char buffer[64] = {0};
@ -210,7 +239,7 @@ static void level_scene_render_func(Scene_t* scene)
sprintf(buffer, "Ladder: %u", p_pstate->ladder_state);
DrawText(buffer, gui_x, 150, 12, BLACK);
}
sprintf(buffer, "Spawn Entity: %u", current_spawn_selection);
sprintf(buffer, "Spawn Entity: %s", get_spawn_selection_string(current_spawn_selection));
DrawText(buffer, gui_x, 240, 12, BLACK);
sprintf(buffer, "Number of Entities: %u", sc_map_size_64v(&scene->ent_manager.entities));
DrawText(buffer, gui_x, 270, 12, BLACK);
@ -317,6 +346,17 @@ static void toggle_block_system(Scene_t* scene)
tilemap.tiles[down_tile].solid = (tilemap.tiles[tile_idx].tile_type != LADDER)? ONE_WAY : NOT_SOLID;
}
break;
case TOGGLE_SPIKE:
if (tilemap.tiles[tile_idx].tile_type == SPIKES)
{
tilemap.tiles[tile_idx].tile_type = EMPTY_TILE;
}
else
{
tilemap.tiles[tile_idx].tile_type = SPIKES;
}
tilemap.tiles[tile_idx].solid = NOT_SOLID;
break;
case SPAWN_CRATE:
spawn_crate(scene, tile_idx, false);
break;
@ -327,7 +367,45 @@ static void toggle_block_system(Scene_t* scene)
spawn_boulder(scene, tile_idx);
break;
}
if (tilemap.tiles[tile_idx].tile_type == SPIKES)
{
// Priority: Down, Up, Left, Right
if (tile_idx + tilemap.width < MAX_N_TILES && tilemap.tiles[tile_idx + tilemap.width].tile_type == SOLID_TILE)
{
tilemap.tiles[tile_idx].offset = (Vector2){0,16};
tilemap.tiles[tile_idx].size = (Vector2){32,16};
}
else if (tile_idx - tilemap.width >= 0 && tilemap.tiles[tile_idx - tilemap.width].tile_type == SOLID_TILE)
{
tilemap.tiles[tile_idx].offset = (Vector2){0,0};
tilemap.tiles[tile_idx].size = (Vector2){32,16};
}
else if (tile_idx % tilemap.width != 0 && tilemap.tiles[tile_idx - 1].tile_type == SOLID_TILE)
{
tilemap.tiles[tile_idx].offset = (Vector2){0,0};
tilemap.tiles[tile_idx].size = (Vector2){16,32};
}
else if ((tile_idx + 1) % tilemap.width != 0 && tilemap.tiles[tile_idx + 1].tile_type == SOLID_TILE)
{
tilemap.tiles[tile_idx].offset = (Vector2){16,0};
tilemap.tiles[tile_idx].size = (Vector2){16,32};
}
else
{
tilemap.tiles[tile_idx].offset = (Vector2){0,16};
tilemap.tiles[tile_idx].size = (Vector2){32,16};
}
}
else
{
tilemap.tiles[tile_idx].offset = (Vector2){0,0};
tilemap.tiles[tile_idx].size = (Vector2){32,32};
}
last_tile_idx = tile_idx;
tilemap.tiles[tile_idx].moveable = (
tilemap.tiles[tile_idx].tile_type == EMPTY_TILE
|| tilemap.tiles[tile_idx].tile_type == SPIKES
);
}
}
else if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON))
@ -431,6 +509,7 @@ void init_level_scene(LevelScene_t* scene)
sc_array_add(&scene->scene.systems, &update_tilemap_system);
sc_array_add(&scene->scene.systems, &hitbox_update_system);
sc_array_add(&scene->scene.systems, &player_crushing_system);
sc_array_add(&scene->scene.systems, &spike_collision_system);
sc_array_add(&scene->scene.systems, &state_transition_update_system);
sc_array_add(&scene->scene.systems, &player_ground_air_transition_system);
sc_array_add(&scene->scene.systems, &sprite_animation_system);
@ -461,13 +540,16 @@ void init_level_scene(LevelScene_t* scene)
{
all_tiles[i].solid = NOT_SOLID;
all_tiles[i].tile_type = EMPTY_TILE;
all_tiles[i].moveable = true;
sc_map_init_64v(&all_tiles[i].entities_set, 16, 0);
all_tiles[i].size = (Vector2){TILE_SIZE, TILE_SIZE};
}
for (size_t i = 0; i < scene->data.tilemap.width; ++i)
{
unsigned int tile_idx = (scene->data.tilemap.height - 1) * scene->data.tilemap.width + i;
all_tiles[tile_idx].solid = SOLID; // for testing
all_tiles[tile_idx].tile_type = SOLID_TILE; // for testing
all_tiles[tile_idx].moveable = false;
}
create_player(&scene->scene.ent_manager, &scene->scene.engine->assets);

View File

@ -3,7 +3,7 @@
uint8_t find_1D_overlap(Vector2 l1, Vector2 l2, float* overlap)
{
// No Overlap
if (l1.y < l2.x || l2.y < l1.x) return 0;
if (l1.y <= l2.x || l2.y <= l1.x) return 0;
if (l1.x >= l2.x && l1.y <= l2.y)
{
@ -14,7 +14,7 @@ uint8_t find_1D_overlap(Vector2 l1, Vector2 l2, float* overlap)
}
//Partial overlap
// x is p1, y is p2
*overlap = (l2.y >= l1.y)? l2.x - l1.y - 1 : l2.y - l1.x + 1;
*overlap = (l2.y > l1.y)? l2.x - l1.y : l2.y - l1.x;
return 1;
}
@ -25,15 +25,15 @@ uint8_t find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector2 tl
Vector2 l1, l2;
uint8_t overlap_x, overlap_y;
l1.x = tl1.x;
l1.y = tl1.x + sz1.x - 1;
l1.y = tl1.x + sz1.x;
l2.x = tl2.x;
l2.y = tl2.x + sz2.x - 1;
l2.y = tl2.x + sz2.x;
overlap_x = find_1D_overlap(l1, l2, &overlap->x);
l1.x = tl1.y;
l1.y = tl1.y + sz1.y - 1;
l1.y = tl1.y + sz1.y;
l2.x = tl2.y;
l2.y = tl2.y + sz2.y - 1;
l2.y = tl2.y + sz2.y;
overlap_y = find_1D_overlap(l1, l2, &overlap->y);
if (overlap_x == 2 && overlap_y == 2) return 2;
@ -49,3 +49,68 @@ bool point_in_AABB(Vector2 point, Rectangle box)
&& point.y < box.y + box.height
);
}
bool line_in_AABB(Vector2 p1, Vector2 p2, Rectangle box)
{
float A = p2.y - p1.y;
float B = p1.x - p2.x;
float C = (p2.x * p1.y) - (p1.x * p2.y);
Vector2 corners[3] =
{
{box.x + box.width, box.y},
{box.x + box.width, box.y + box.height},
{box.x, box.y + box.height},
};
float F = (A * box.x + B * box.y + C);
bool collide = false;
uint8_t last_mode = 0;
if (fabs(F) < 1e-3)
{
last_mode = 0;
}
else
{
last_mode = (F > 0) ? 1 : 2;
}
for (uint8_t i = 0; i < 3; ++i)
{
F = (A * corners[i].x + B * corners[i].y + C);
uint8_t mode = 0;
if (fabs(F) < 1e-3)
{
mode = 0;
}
else
{
mode = (F > 0) ? 1 : 2;
}
if (mode != last_mode)
{
collide = true;
break;
}
last_mode = mode;
}
if (!collide) return false;
//Projection check
Vector2 overlap = {0};
Vector2 l1, l2;
l1.x = p1.x;
l1.y = p2.x;
l2.x = box.x;
l2.y = box.x + box.width;
find_1D_overlap(l1, l2, &overlap.x);
l1.x = p1.y;
l1.y = p2.y;
l2.x = box.y;
l2.y = box.y + box.height;
find_1D_overlap(l1, l2, &overlap.y);
return (overlap.x != 0 && overlap.y != 0);
}

View File

@ -6,4 +6,5 @@
uint8_t find_1D_overlap(Vector2 l1, Vector2 l2, float* overlap);
uint8_t find_AABB_overlap(const Vector2 tl1, const Vector2 sz1, const Vector2 tl2, const Vector2 sz2, Vector2* overlap);
bool point_in_AABB(Vector2 point, Rectangle box);
bool line_in_AABB(Vector2 p1, Vector2 p2, Rectangle box);
#endif // __AABB_H

View File

@ -5,7 +5,6 @@
#include "constants.h"
#include <stdio.h>
static const Vector2 TILE_SZ = {TILE_SIZE, TILE_SIZE};
static const Vector2 GRAVITY = {0, GRAV_ACCEL};
static const Vector2 UPTHRUST = {0, -GRAV_ACCEL * 1.1};
typedef enum AnchorPoint {
@ -52,15 +51,30 @@ static uint8_t check_collision(const CollideEntity_t* ent, TileGrid_t* grid, boo
{
if (tile_x >= grid->width) return 0;
unsigned int tile_idx = tile_y*grid->width + tile_x;
if (grid->tiles[tile_idx].solid == SOLID) return 1;
Vector2 overlap;
if (grid->tiles[tile_idx].solid == SOLID)
{
if (find_AABB_overlap(
(Vector2){ent->bbox.x, ent->bbox.y},
(Vector2){ent->bbox.width, ent->bbox.height},
(Vector2){tile_x * TILE_SIZE + grid->tiles[tile_idx].offset.x, tile_y * TILE_SIZE + grid->tiles[tile_idx].offset.y},
grid->tiles[tile_idx].size,
&overlap
))
{
return 1;
}
}
if (check_oneway && grid->tiles[tile_idx].solid == ONE_WAY)
{
find_AABB_overlap(
(Vector2){ent->bbox.x, ent->bbox.y},
(Vector2){ent->bbox.width, ent->bbox.height},
(Vector2){tile_x * TILE_SIZE, tile_y * TILE_SIZE}, TILE_SZ, &overlap
(Vector2){tile_x * TILE_SIZE + grid->tiles[tile_idx].offset.x, tile_y * TILE_SIZE + grid->tiles[tile_idx].offset.y},
grid->tiles[tile_idx].size,
&overlap
);
//For one-way platform, check for vectical collision, only return true for up direction
@ -95,6 +109,71 @@ static uint8_t check_collision(const CollideEntity_t* ent, TileGrid_t* grid, boo
return 0;
}
static uint8_t check_collision_line(const CollideEntity_t* ent, TileGrid_t* grid, bool check_oneway)
{
Vector2 p1 = {ent->bbox.x, ent->bbox.y};
Vector2 p2 = {ent->bbox.x + ent->bbox.width - 1, ent->bbox.y + ent->bbox.height - 1};
for(unsigned int tile_y = ent->area.tile_y1; tile_y <= ent->area.tile_y2; tile_y++)
{
if (tile_y >= grid->height) return 0;
for(unsigned int tile_x = ent->area.tile_x1; tile_x <= ent->area.tile_x2; tile_x++)
{
if (tile_x >= grid->width) return 0;
unsigned int tile_idx = tile_y*grid->width + tile_x;
if (grid->tiles[tile_idx].solid == SOLID)
{
Rectangle tile_rec = {
.x = tile_x * TILE_SIZE + grid->tiles[tile_idx].offset.x,
.y = tile_y * TILE_SIZE + grid->tiles[tile_idx].offset.y,
.width = grid->tiles[tile_idx].size.x,
.height = grid->tiles[tile_idx].size.y
};
if ( line_in_AABB(p1, p2, tile_rec) ) return 1;
}
if (check_oneway && grid->tiles[tile_idx].solid == ONE_WAY)
{
Rectangle tile_rec = {
.x = tile_x * TILE_SIZE + grid->tiles[tile_idx].offset.x,
.y = tile_y * TILE_SIZE + grid->tiles[tile_idx].offset.y,
.width = grid->tiles[tile_idx].size.x,
.height = grid->tiles[tile_idx].size.y
};
bool collide = line_in_AABB(p1, p2, tile_rec);
//For one-way platform, check for vectical collision, only return true for up direction
if (collide && ent->prev_bbox.y + ent->prev_bbox.height - 1 < tile_y * TILE_SIZE) return 1;
}
Entity_t* p_other_ent;
sc_map_foreach_value(&grid->tiles[tile_idx].entities_set, p_other_ent)
{
if (ent->p_ent->m_id == p_other_ent->m_id) continue;
if (!ent->p_ent->m_alive) continue;
CTransform_t *p_ctransform = get_component(p_other_ent, CTRANSFORM_COMP_T);
CBBox_t *p_bbox = get_component(p_other_ent, CBBOX_COMP_T);
if (p_bbox == NULL || p_ctransform == NULL) continue;
//if (p_bbox->solid && !p_bbox->fragile)
if (p_bbox->solid)
{
Rectangle box = {
.x = p_ctransform->position.x,
.y = p_ctransform->position.y,
.width = p_bbox->size.x,
.height = p_bbox->size.y,
};
if ( line_in_AABB(p1, p2, box) )
{
return (p_bbox->fragile) ? 2 : 1;
}
}
}
}
}
return 0;
}
// TODO: This should be a point collision check, not an AABB check
static bool check_collision_offset(
Entity_t* p_ent, Vector2 pos, Vector2 bbox_sz,
@ -184,7 +263,7 @@ static bool check_collision_and_move(
&& (p_ct->prev_position.y + p_bbox->size.y - 1 < other_pos->y))
)
{
//if (!check_collision_at(ent, p_ct->position, p_bbox->size, tilemap, offset))
//if (!check_collision_offset(ent, p_ct->position, p_bbox->size, tilemap, offset))
{
p_ct->position = Vector2Add(p_ct->position, offset);
}
@ -241,48 +320,60 @@ collision_end:
static uint8_t check_bbox_edges(
TileGrid_t* tilemap,
Entity_t* p_ent, Vector2 pos, Vector2 prev_pos, Vector2 bbox
Entity_t* p_ent, Vector2 pos, Vector2 prev_pos, Vector2 bbox,
bool ignore_fragile
)
{
uint8_t detected = 0;
// Too lazy to adjust the tile area to check, so just make a big one
CollideEntity_t ent =
{
.p_ent = p_ent,
.bbox = (Rectangle){pos.x - 1, pos.y, bbox.x, bbox.y},
.bbox = (Rectangle){pos.x - 1, pos.y, 1, bbox.y},
.prev_bbox = (Rectangle){pos.x, pos.y, bbox.x, bbox.y},
.area = (TileArea_t){
.tile_x1 = (pos.x - 1) / TILE_SIZE,
.tile_y1 = (pos.y) / TILE_SIZE,
.tile_x2 = (pos.x - 1) / TILE_SIZE,
.tile_y2 = (pos.y + bbox.y - 1) / TILE_SIZE,
.tile_y1 = (pos.y - 1) / TILE_SIZE,
.tile_x2 = (pos.x + bbox.x) / TILE_SIZE,
.tile_y2 = (pos.y + bbox.y) / TILE_SIZE,
}
};
// TODO: Handle one-way platform
// Left
detected |= (check_collision(&ent, tilemap, false) ? 1 : 0) << 3;
uint8_t collide_type = check_collision_line(&ent, tilemap, false);
if (collide_type == 1 || (collide_type == 2 && !ignore_fragile))
{
detected |= 1 << 3;
}
//Right
ent.bbox.x += 2; // 2 to account for the previous subtraction
ent.area.tile_x1 = (pos.x + bbox.x) / TILE_SIZE;
ent.area.tile_x2 = ent.area.tile_x1;
detected |= (check_collision(&ent, tilemap, false) ? 1 : 0) << 2;
ent.bbox.x = pos.x + bbox.x + 1; // 2 to account for the previous subtraction
collide_type = check_collision_line(&ent, tilemap, false);
if (collide_type == 1 || (collide_type == 2 && !ignore_fragile))
{
detected |= 1 << 2;
}
// Up
ent.bbox.x -= 2;
ent.bbox.y--;
ent.area.tile_x1 = (pos.x) / TILE_SIZE,
ent.area.tile_x2 = (pos.x + bbox.x - 1) / TILE_SIZE,
ent.area.tile_y1 = (pos.y - 1) / TILE_SIZE,
ent.area.tile_y2 = ent.area.tile_y1;
detected |= (check_collision(&ent, tilemap, false) ? 1 : 0) << 1;
ent.bbox.x = pos.x;
ent.bbox.y = pos.y - 1;
ent.bbox.width = bbox.x;
ent.bbox.height = 1;
collide_type = check_collision_line(&ent, tilemap, false);
if (collide_type == 1 || (collide_type == 2 && !ignore_fragile))
{
detected |= 1 << 1;
}
// Down
ent.bbox.y += 2;
ent.area.tile_y1 = (pos.y + bbox.y) / TILE_SIZE,
ent.area.tile_y2 = ent.area.tile_y1;
detected |= (check_collision(&ent, tilemap, true) ? 1 : 0);
ent.bbox.y = pos.y + bbox.y + 1;
collide_type = check_collision_line(&ent, tilemap, true);
if (collide_type == 1 || (collide_type == 2 && !ignore_fragile))
{
detected |= 1;
}
return detected;
}
@ -613,60 +704,145 @@ void player_bbox_update_system(Scene_t* scene)
void player_crushing_system(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
TileGrid_t tilemap = data->tilemap;
Entity_t* p_player;
sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_player)
{
CTransform_t* p_ctransform = get_component(p_player, CTRANSFORM_COMP_T);
CBBox_t* p_bbox = get_component(p_player, CBBOX_COMP_T);
CollideEntity_t ent =
uint8_t edges = check_bbox_edges(
&data->tilemap, p_player,
p_ctransform->position, p_ctransform->prev_position, p_bbox->size, true
);
// There is a second check for to ensure that there is an solid entity/tile overlapping the player bbox
// This is to prevent crushing by perfectly fitting in a gap (imagine a size 32 player in between two tiles)
// or any scenario where the edge check is just fringing, not overlapping
if ((edges & 0b0011) == 0b0011)
{
.p_ent = p_player,
.bbox = (Rectangle){p_ctransform->position.x, p_ctransform->position.y, p_bbox->size.x, p_bbox->size.y},
.prev_bbox = (Rectangle){p_ctransform->prev_velocity.x, p_ctransform->prev_position.y, p_bbox->size.x, p_bbox->size.y},
.area = (TileArea_t){
.tile_x1 = (p_ctransform->position.x) / TILE_SIZE,
.tile_y1 = (p_ctransform->position.y) / TILE_SIZE,
.tile_x2 = (p_ctransform->position.x) / TILE_SIZE,
.tile_y2 = (p_ctransform->position.y + p_bbox->size.y - 1) / TILE_SIZE,
},
};
uint8_t collide = 0;
CollideEntity_t ent =
{
.p_ent = p_player,
.bbox = (Rectangle){p_ctransform->position.x, p_ctransform->position.y, p_bbox->size.x, 1},
.prev_bbox = (Rectangle){p_ctransform->position.x, p_ctransform->position.y, p_bbox->size.x, p_bbox->size.y},
.area = (TileArea_t){
.tile_x1 = (p_ctransform->position.x) / TILE_SIZE,
.tile_y1 = (p_ctransform->position.y) / TILE_SIZE,
.tile_x2 = (p_ctransform->position.x + p_bbox->size.x - 1) / TILE_SIZE,
.tile_y2 = (p_ctransform->position.y + p_bbox->size.y - 1) / TILE_SIZE,
}
};
uint8_t collide_type = check_collision_line(&ent, &data->tilemap, false);
if (collide_type == 1)
{
collide |= 1 << 1;
}
ent.bbox.y = p_ctransform->position.y + p_bbox->size.y;
collide_type = check_collision_line(&ent, &data->tilemap, true);
if (collide_type == 1)
{
collide |= 1;
}
// Mostly identical to edge check function
// Except we want collision instead of just touching
uint8_t detected = 0;
// Left
detected |= (check_collision(&ent, &tilemap, false) ? 1 : 0);
//Right
ent.area.tile_x1 = (p_ctransform->position.x + p_bbox->size.x - 1) / TILE_SIZE;
ent.area.tile_x2 = ent.area.tile_x1;
detected |= (check_collision(&ent, &tilemap, false) ? 1 : 0) << 1;
if (detected == 0b11)
{
p_player->m_alive = false;
return;
if (collide != 0)
{
p_player->m_alive = false;
return;
}
}
detected = 0;
// Up
ent.area.tile_x1 = (p_ctransform->position.x) / TILE_SIZE,
ent.area.tile_y1 = (p_ctransform->position.y - 1) / TILE_SIZE,
ent.area.tile_y2 = ent.area.tile_y1;
detected |= (check_collision(&ent, &tilemap, false) ? 1 : 0) << 1;
// Down
ent.area.tile_y1 = (p_ctransform->position.y + p_bbox->size.y - 1) / TILE_SIZE,
ent.area.tile_y2 = ent.area.tile_y1;
detected |= (check_collision(&ent, &tilemap, true) ? 1 : 0);
//if (check_collision(&ent, &tilemap, false) == 1)
if (detected == 0b11)
if ((edges & 0b1100) == 0b1100)
{
p_player->m_alive = false;
return;
uint8_t collide = 0;
CollideEntity_t ent =
{
.p_ent = p_player,
.bbox = (Rectangle){p_ctransform->position.x, p_ctransform->position.y, 1, p_bbox->size.y},
.prev_bbox = (Rectangle){p_ctransform->position.x, p_ctransform->position.y, p_bbox->size.x, p_bbox->size.y},
.area = (TileArea_t){
.tile_x1 = (p_ctransform->position.x) / TILE_SIZE,
.tile_y1 = (p_ctransform->position.y) / TILE_SIZE,
.tile_x2 = (p_ctransform->position.x + p_bbox->size.x - 1) / TILE_SIZE,
.tile_y2 = (p_ctransform->position.y + p_bbox->size.y - 1) / TILE_SIZE,
}
};
// Left
uint8_t collide_type = check_collision_line(&ent, &data->tilemap, false);
if (collide_type == 1)
{
collide |= 1 << 1;
}
//Right
ent.bbox.x = p_ctransform->position.x + p_bbox->size.x; // 2 to account for the previous subtraction
collide_type = check_collision_line(&ent, &data->tilemap, false);
if (collide_type == 1)
{
collide |= 1;
}
if (collide != 0)
{
p_player->m_alive = false;
return;
}
}
}
}
void spike_collision_system(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
TileGrid_t tilemap = data->tilemap;
//Entity_t* p_player;
CBBox_t* p_bbox;
unsigned int ent_idx;
sc_map_foreach(&scene->ent_manager.component_map[CBBOX_COMP_T], ent_idx, p_bbox)
{
Entity_t* p_ent = get_entity(&scene->ent_manager, ent_idx);
CTransform_t* p_ctransform = get_component(p_ent, CTRANSFORM_COMP_T);
unsigned int tile_x1 = (p_ctransform->position.x) / TILE_SIZE;
unsigned int tile_y1 = (p_ctransform->position.y) / TILE_SIZE;
unsigned int tile_x2 = (p_ctransform->position.x + p_bbox->size.x - 1) / TILE_SIZE;
unsigned int tile_y2 = (p_ctransform->position.y + p_bbox->size.y - 1) / TILE_SIZE;
Vector2 overlap;
for (unsigned int tile_y = tile_y1; tile_y <= tile_y2; tile_y++)
{
for (unsigned int tile_x = tile_x1; tile_x <= tile_x2; tile_x++)
{
unsigned int tile_idx = tile_y * tilemap.width + tile_x;
if(tilemap.tiles[tile_idx].tile_type == SPIKES)
{
uint8_t collide = find_AABB_overlap(
p_ctransform->position, p_bbox->size,
(Vector2){
tile_x * TILE_SIZE + tilemap.tiles[tile_idx].offset.x,
tile_y * TILE_SIZE + tilemap.tiles[tile_idx].offset.y
},
tilemap.tiles[tile_idx].size,
&overlap);
if (collide)
{
if (p_ent->m_tag == PLAYER_ENT_TAG)
{
p_ent->m_alive = false;
return;
}
else
{
tilemap.tiles[tile_idx].tile_type = EMPTY_TILE;
}
}
}
}
}
}
}
@ -691,8 +867,8 @@ void tile_collision_system(Scene_t* scene)
// exclude self
// This has an extra pixel when gathering potential collision, just to avoid missing any
// This is only done here, collision methods do not have this
unsigned int tile_x1 = (p_ctransform->position.x) / TILE_SIZE;
unsigned int tile_y1 = (p_ctransform->position.y) / TILE_SIZE;
unsigned int tile_x1 = (p_ctransform->position.x - 1) / TILE_SIZE;
unsigned int tile_y1 = (p_ctransform->position.y - 1) / TILE_SIZE;
unsigned int tile_x2 = (p_ctransform->position.x + p_bbox->size.x) / TILE_SIZE;
unsigned int tile_y2 = (p_ctransform->position.y + p_bbox->size.y) / TILE_SIZE;
@ -704,12 +880,12 @@ void tile_collision_system(Scene_t* scene)
if(tilemap.tiles[tile_idx].tile_type != EMPTY_TILE)
{
Vector2 other;
other.x = (tile_idx % tilemap.width) * TILE_SIZE;
other.y = (tile_idx / tilemap.width) * TILE_SIZE; // Precision loss is intentional
other.x = (tile_idx % tilemap.width) * TILE_SIZE + tilemap.tiles[tile_idx].offset.x;
other.y = (tile_idx / tilemap.width) * TILE_SIZE + tilemap.tiles[tile_idx].offset.y; // Precision loss is intentional
check_collision_and_move(
&tilemap, p_ent,
&other, TILE_SZ, tilemap.tiles[tile_idx].solid
&other, tilemap.tiles[tile_idx].size, tilemap.tiles[tile_idx].solid
);
}
@ -742,7 +918,7 @@ void tile_collision_system(Scene_t* scene)
// Post movement edge check to zero out velocity
uint8_t edges = check_bbox_edges(
&data->tilemap, p_ent,
p_ctransform->position, p_ctransform->prev_position, p_bbox->size
p_ctransform->position, p_ctransform->prev_position, p_bbox->size, false
);
if (edges & (1<<3))
{
@ -868,7 +1044,7 @@ void global_external_forces_system(Scene_t* scene)
// Zero out acceleration for contacts with sturdy entites and tiles
uint8_t edges = check_bbox_edges(
&data->tilemap, p_ent,
p_ctransform->position, p_ctransform->prev_position, p_bbox->size
p_ctransform->position, p_ctransform->prev_position, p_bbox->size, false
);
if (edges & (1<<3))
{
@ -970,10 +1146,7 @@ void moveable_update_system(Scene_t* scene)
{
unsigned int tile_idx1 = tile_y * tilemap.width + tile_x;
unsigned int tile_idx2 = tile_y2 * tilemap.width + tile_x;
if (
tilemap.tiles[tile_idx1].tile_type == EMPTY_TILE
&& tilemap.tiles[tile_idx2].tile_type == EMPTY_TILE
)
if ( tilemap.tiles[tile_idx1].moveable && tilemap.tiles[tile_idx2].moveable )
{
bool any_solid = false;
unsigned int idx_to_check;
@ -1002,9 +1175,7 @@ void moveable_update_system(Scene_t* scene)
{
unsigned int tile_idx1 = tile_y * tilemap.width + tile_x;
unsigned int tile_idx2 = tile_y2 * tilemap.width + tile_x;
if (tilemap.tiles[tile_idx1].tile_type == EMPTY_TILE
&& tilemap.tiles[tile_idx2].tile_type == EMPTY_TILE
)
if ( tilemap.tiles[tile_idx1].moveable && tilemap.tiles[tile_idx2].moveable )
{
bool any_solid = false;
unsigned int idx_to_check;
@ -1115,7 +1286,7 @@ void player_pushing_system(Scene_t* scene)
{
unsigned int target_tile_idx = tile_y * tilemap.width + tile_x;
if (
tilemap.tiles[target_tile_idx].tile_type == EMPTY_TILE
tilemap.tiles[target_tile_idx].moveable
&& sc_map_size_64v(&tilemap.tiles[target_tile_idx].entities_set) == 0
)
{
@ -1458,15 +1629,18 @@ void boulder_destroy_wooden_tile_system(Scene_t* scene)
{
tilemap.tiles[tile_idx].tile_type = EMPTY_TILE;
tilemap.tiles[tile_idx].solid = NOT_SOLID;
tilemap.tiles[tile_idx].moveable = true;
if (tile_x > 0 && tilemap.tiles[tile_idx - 1].tile_type == ONEWAY_TILE)
{
tilemap.tiles[tile_idx - 1].tile_type = EMPTY_TILE;
tilemap.tiles[tile_idx - 1].solid = NOT_SOLID;
tilemap.tiles[tile_idx - 1].moveable = true;
}
if (tile_x < tilemap.width && tilemap.tiles[tile_idx + 1].tile_type == ONEWAY_TILE)
{
tilemap.tiles[tile_idx + 1].tile_type = EMPTY_TILE;
tilemap.tiles[tile_idx + 1].solid = NOT_SOLID;
tilemap.tiles[tile_idx + 1].moveable = true;
}
}
}

View File

@ -22,4 +22,5 @@ void boulder_destroy_wooden_tile_system(Scene_t* scene);
void camera_update_system(Scene_t* scene);
void player_dir_reset_system(Scene_t* scene);
void player_respawn_system(Scene_t* scene);
void spike_collision_system(Scene_t* scene);
#endif // __GAME_SYSTEMS_H

View File

@ -15,9 +15,10 @@ typedef enum TileType {
EMPTY_TILE = 0,
SOLID_TILE,
ONEWAY_TILE,
LADDER
LADDER,
SPIKES,
} TileType_t;
#define MAX_TILE_TYPES 4
#define MAX_TILE_TYPES 5
typedef enum SolidType
{
NOT_SOLID = 0,
@ -31,6 +32,9 @@ typedef struct Tile {
SolidType_t solid;
unsigned int water_level;
struct sc_map_64v entities_set;
Vector2 offset;
Vector2 size;
bool moveable;
}Tile_t;
typedef struct TileGrid {

View File

@ -6,6 +6,44 @@
#include <setjmp.h>
#include <cmocka.h>
static void test_line_AABB(void **state)
{
(void) state;
Vector2 p1 = {0, 0};
Vector2 p2 = {20, 20};
Rectangle box = {5, 0, 10, 20};
assert_true(line_in_AABB(p1, p2, box));
p1.y = 20;
assert_false(line_in_AABB(p1, p2, box));
p1.y = 19;
p2 = (Vector2){19, 19};
assert_true(line_in_AABB(p1, p2, box));
p1.y = 0;
p2.y = 0;
assert_true(line_in_AABB(p1, p2, box));
p1 = (Vector2){5, 0};
p2 = (Vector2){5, 10};
assert_true(line_in_AABB(p1, p2, box));
p1 = (Vector2){14, 0};
p2 = (Vector2){14, 10};
assert_true(line_in_AABB(p1, p2, box));
p1 = (Vector2){15, 0};
p2 = (Vector2){15, 10};
assert_false(line_in_AABB(p1, p2, box));
p1 = (Vector2){0, 30};
p2 = (Vector2){6, 35};
assert_false(line_in_AABB(p1, p2, box));
}
static void test_point_AABB(void **state)
{
(void) state;
@ -64,16 +102,14 @@ static void test_1D_overlap(void **state)
assert_int_equal(find_1D_overlap(a, b, &overlap), 0);
a.y = 6;
assert_int_equal(find_1D_overlap(a, b, &overlap), 0);
assert_int_equal(find_1D_overlap(b, a, &overlap), 0);
a.y = 7;
assert_int_equal(find_1D_overlap(a, b, &overlap), 1);
assert_float_equal(overlap, -1, 1e-5);
assert_int_equal(find_1D_overlap(b, a, &overlap), 1);
assert_float_equal(overlap, 1, 1e-5);
a.y = 7;
assert_int_equal(find_1D_overlap(a, b, &overlap), 1);
assert_float_equal(overlap, -2, 1e-5);
assert_int_equal(find_1D_overlap(b, a, &overlap), 1);
assert_float_equal(overlap, 2, 1e-5);
a.x = 7;
a.y = 9;
@ -86,6 +122,7 @@ int main(void)
cmocka_unit_test(test_1D_overlap),
cmocka_unit_test(test_AABB_overlap),
cmocka_unit_test(test_point_AABB),
cmocka_unit_test(test_line_AABB),
};
return cmocka_run_group_tests(tests, NULL, NULL);