diff --git a/game/NOTES.md b/game/NOTES.md new file mode 100644 index 0000000..a6d7371 --- /dev/null +++ b/game/NOTES.md @@ -0,0 +1,7 @@ +Some notes on the game + +# Position and velocity + +All positions are represented in fixed-point fashion, with a precision of 256. + +For instance, a position or distance of 8 pixels is represented as `0x800`. (2048) \ No newline at end of file diff --git a/game/game.c b/game/game.c index 94fa5f0..be9c206 100644 --- a/game/game.c +++ b/game/game.c @@ -4,9 +4,20 @@ #include "map/game_map.h" #include "game.h" #include "sys/sys.h" +#include uint32_t game_frame; +game_bbox game_player_bbox = { + .x=0x9000, + // .y=0x5800, + .y=0x0000, + .w=0x1000, + .h=0x1000, +}; +sys_i32 game_player_dx = 0; +sys_i32 game_player_dy = 0; + const char* game_title() { return "Croc Party!"; } @@ -23,6 +34,19 @@ void game_destroy() { void game_update() { game_frame += 1; + + game_player_dy += 1; + + if (device_buttons[0]) { game_player_dx -= 16; } + if (device_buttons[1]) { game_player_dx += 16; } + + game_collision collision = game_collision_move_to_contact( + &game_player_bbox, + game_player_dx, + game_player_dy + ); + if (collision.collided_x) { game_player_dx = 0; } + if (collision.collided_y) { game_player_dy = 0; } } void game_draw() { @@ -31,7 +55,10 @@ void game_draw() { sys_map_draw(map_game_map, spr_game_tiles, 0, 0, 0, 0, 32, 18); sys_sprite_draw_ext( spr_game_player, - (game_frame / 8) % 4 * 2, 144, 88, 2, 2, + (game_frame / 8) % 4 * 2, + game_player_bbox.x / 0x100, + game_player_bbox.y / 0x100, + 2, 2, false, false ); } diff --git a/game/game.h b/game/game.h index 04fff7b..02e3a79 100644 --- a/game/game.h +++ b/game/game.h @@ -1,6 +1,7 @@ #ifndef CROCPARTY_GAME_H #define CROCPARTY_GAME_H +#include "game_collision.h" #include "game_palette.h" const char* game_title(); diff --git a/game/game_collision.c b/game/game_collision.c new file mode 100644 index 0000000..9b7923e --- /dev/null +++ b/game/game_collision.c @@ -0,0 +1,122 @@ +#include +#include "sys/sys.h" +#include "game.h" +#include "map/game_map.h" + +bool game_collision_is_occlusive(sys_maptile tile) { + if (tile >= 0 && tile < 30) { return true; } + if (tile >= 60 && tile < 90) { return true; } + return false; +} + +bool game_collision_can_move(game_bbox body, sys_i32 dx, sys_i32 dy) { + sys_i32 x = body.x + dx; + sys_i32 y = body.y + dy; + + // the player can never leave the map + if (x < 0) { return false; } + if (y < 0) { return false; } + if (x + body.w > map_game_map.width * TILE_SZ_MICROPIXEL) { + return false; + } + if (y + body.h > map_game_map.height * TILE_SZ_MICROPIXEL) { + return false; + } + + // TODO: Collide with blocks + // If two collides with a block, check if one collides with it. + // The collision only matters if one doesn't. + return true; +} + +#define round_down(x, incr) (x) - (x) % (incr) +#define round_up(x, incr) round_down((x) + (incr) - 1, incr) + +game_collision game_collision_move_to_contact( + game_bbox* body, sys_i32 total_dx, sys_i32 total_dy +) { + sys_i32 src_x = body->x; + sys_i32 src_y = body->y; + game_collision collision = { + .collided_x=false, + .collided_y=false, + .distance_x=0, + .distance_y=0, + }; + sys_i32 tar_x = body->x + total_dx; + sys_i32 tar_y = body->y + total_dy; + + while (true) { + // calculate how much we still need to move + int32_t remaining_dx = tar_x - body->x; + int32_t remaining_dy = tar_y - body->y; + + // if we don't need to move, stop + if (remaining_dx == 0 && remaining_dy == 0) { + collision.distance_x = body->x - src_x; + collision.distance_y = body->x - src_y; + return collision; + } + + // figure out where we are + sys_i32 body_x0 = body->x; + sys_i32 body_y0 = body->y; + sys_i32 body_x1 = body->x + body->w; + sys_i32 body_y1 = body->y + body->h; + + // figure out what tile we're still in + sys_i32 tile_x0 = round_down(body_x0, TILE_SZ_MICROPIXEL); + sys_i32 tile_y0 = round_down(body_y0, TILE_SZ_MICROPIXEL); + sys_i32 tile_x1 = round_up(body_x1, TILE_SZ_MICROPIXEL); + sys_i32 tile_y1 = round_up(body_y1, TILE_SZ_MICROPIXEL); + + // but we want the _next_ tile + if (tile_x0 == body_x0) { tile_x0 -= TILE_SZ_MICROPIXEL; } + if (tile_y0 == body_y0) { tile_y0 -= TILE_SZ_MICROPIXEL; } + if (tile_x1 == body_x1) { tile_x1 += TILE_SZ_MICROPIXEL; } + if (tile_y1 == body_y1) { tile_y1 += TILE_SZ_MICROPIXEL; } + + // length of an x axis move + int step_dx = + remaining_dx < 0 ? + // line up our left side with the tile to our left + // not exceeding dx + sys_max_i32(remaining_dx, tile_x0 - body_x0) : + remaining_dx > 0 ? + // line up our right side with the tile to our right + // not exceeding dx + sys_min_i32(remaining_dx, tile_x1 - body_x1) : + // dx == 0 + INT32_MAX ; + + int step_dy = + remaining_dy < 0 ? + // line up our top side with the tile above us + // not exceeding dy + sys_max_i32(remaining_dy, tile_y0 - body_y0) : + remaining_dy > 0 ? + // line up our bottom side with the tile below us + // not exceeding dy + sys_min_i32(remaining_dy, tile_y1 - body_y1) : + INT32_MAX; + + // there must be a viable move in some direction + assert(step_dx != INT32_MAX || step_dy != INT32_MAX); + + if (sys_abs_i32(step_dx) < sys_abs_i32(step_dy)) { + if (game_collision_can_move(*body, step_dx, 0)) { + body->x += step_dx; // take the tiny move + } else { + tar_x = body->x; // stop trying to move + collision.collided_x = true; + } + } else { + if (game_collision_can_move(*body, 0, step_dy)) { + body->y += step_dy; // take the tiny move + } else { + tar_y = body->y; // stop trying to move + collision.collided_y = true; + } + } + } +} \ No newline at end of file diff --git a/game/game_collision.h b/game/game_collision.h new file mode 100644 index 0000000..1b8d012 --- /dev/null +++ b/game/game_collision.h @@ -0,0 +1,27 @@ +#ifndef GAME_COLLISION_H +#define GAME_COLLISION_H + +#include +#include "sys/sys.h" + +#define TILE_SZ_MICROPIXEL 0x800 + +typedef struct { + sys_i32 x; + sys_i32 y; + sys_i32 w; + sys_i32 h; +} game_bbox; + +typedef struct { + bool collided_x; + bool collided_y; + sys_i32 distance_x; + sys_i32 distance_y; +} game_collision; + +bool game_collision_is_occlusive(sys_maptile tile); +bool game_collision_can_move(game_bbox body, sys_i32 dx, sys_i32 dy); +game_collision game_collision_move_to_contact(game_bbox* body, sys_i32 x, sys_i32 y); + +#endif \ No newline at end of file