Add origin point to sprites, WASD controls camera, debug draw rects, todo list

This commit is contained in:
Joseph Ferano 2024-01-01 16:38:41 +07:00
parent 380361431f
commit ceca65fb46
4 changed files with 100 additions and 32 deletions

View File

@ -9,6 +9,7 @@
#define ANIM_KNIGHT_ATTACK_BACK1 6 #define ANIM_KNIGHT_ATTACK_BACK1 6
#define ANIM_KNIGHT_ATTACK_BACK2 7 #define ANIM_KNIGHT_ATTACK_BACK2 7
const u16 cell_size = 192;
SpriteSheet knight = { SpriteSheet knight = {
.name = "blue_knight", .name = "blue_knight",
.texture = {0}, .texture = {0},
@ -16,61 +17,62 @@ SpriteSheet knight = {
.size = { 1152, 1536 }, .size = { 1152, 1536 },
}; };
const u16 cell_size = 192; const Vector2 knight_origin = {cell_size / 2, cell_size / 2};
SpriteAnimation knight_idle = { SpriteAnimation knight_idle = {
.name = "idle", .name = "idle",
.total_frames = 6, .total_frames = 6,
.loop = true, .loop = true,
.src_rect = { 0, cell_size * 0, 192, 192 }, .src_rect = { 0, cell_size * 0, cell_size, cell_size },
}; };
SpriteAnimation knight_run = { SpriteAnimation knight_run = {
.name = "run", .name = "run",
.total_frames = 6, .total_frames = 6,
.loop = true, .loop = true,
.src_rect = { 0, cell_size * 1, 192, 192 }, .src_rect = { 0, cell_size * 1, cell_size, cell_size },
}; };
SpriteAnimation knight_attack_side1 = { SpriteAnimation knight_attack_side1 = {
.name = "attack_side1", .name = "attack_side1",
.total_frames = 6, .total_frames = 6,
.loop = false, .loop = false,
.src_rect = { 0, cell_size * 2, 192, 192 }, .src_rect = { 0, cell_size * 2, cell_size, cell_size },
}; };
SpriteAnimation knight_attack_side2 = { SpriteAnimation knight_attack_side2 = {
.name = "attack_side2", .name = "attack_side2",
.total_frames = 6, .total_frames = 6,
.loop = false, .loop = false,
.src_rect = { 0, cell_size * 3, 192, 192 }, .src_rect = { 0, cell_size * 3, cell_size, cell_size },
}; };
SpriteAnimation knight_attack_front1 = { SpriteAnimation knight_attack_front1 = {
.name = "attack_front1", .name = "attack_front1",
.total_frames = 6, .total_frames = 6,
.loop = false, .loop = false,
.src_rect = { 0, cell_size * 4, 192, 192 }, .src_rect = { 0, cell_size * 4, cell_size, cell_size },
}; };
SpriteAnimation knight_attack_front2 = { SpriteAnimation knight_attack_front2 = {
.name = "attack_front2", .name = "attack_front2",
.total_frames = 6, .total_frames = 6,
.loop = false, .loop = false,
.src_rect = { 0, cell_size * 5, 192, 192 }, .src_rect = { 0, cell_size * 5, cell_size, cell_size },
}; };
SpriteAnimation knight_attack_back1 = { SpriteAnimation knight_attack_back1 = {
.name = "attack_back1", .name = "attack_back1",
.total_frames = 6, .total_frames = 6,
.loop = false, .loop = false,
.src_rect = { 0, cell_size * 6, 192, 192 }, .src_rect = { 0, cell_size * 6, cell_size, cell_size },
}; };
SpriteAnimation knight_attack_back2 = { SpriteAnimation knight_attack_back2 = {
.name = "attack_back2", .name = "attack_back2",
.total_frames = 6, .total_frames = 6,
.loop = false, .loop = false,
.src_rect = { 0, cell_size * 7, 192, 192 }, .src_rect = { 0, cell_size * 7, cell_size, cell_size },
}; };
SpriteAnimation knight_anims[8]; SpriteAnimation knight_anims[8];

4
lib.h
View File

@ -18,7 +18,7 @@ typedef char byte;
typedef ptrdiff_t size; typedef ptrdiff_t size;
typedef size_t usize; typedef size_t usize;
typedef Vector2 Position; typedef Vector2 Point;
typedef struct Size { typedef struct Size {
float width; float width;
@ -26,7 +26,7 @@ typedef struct Size {
} Size; } Size;
typedef struct Rect { typedef struct Rect {
Position position; Point position;
Size size; Size size;
} Rect; } Rect;

83
main.c
View File

@ -9,9 +9,10 @@
#define MAX_ANIMATION_PLAYBACKS 64 #define MAX_ANIMATION_PLAYBACKS 64
#include "sprites.h" #include "sprites.h"
#include "lib.h"
#include "game_data.h" #include "game_data.h"
#define DEBUG_MODE_ENABLED
typedef enum KnightState { typedef enum KnightState {
KNIGHT_IDLE = 0, KNIGHT_IDLE = 0,
KNIGHT_RUNNING = 1, KNIGHT_RUNNING = 1,
@ -30,7 +31,8 @@ typedef enum Direction {
} Direction; } Direction;
typedef struct { typedef struct {
Vector2 position; Point position;
Point origin;
Vector2 velocity; Vector2 velocity;
Direction look_dir; Direction look_dir;
KnightState state; KnightState state;
@ -39,9 +41,11 @@ typedef struct {
typedef struct { typedef struct {
int frame_count; int frame_count;
Knight knight; Knight knight;
Rectangle *sprite_rects; // Rectangle* sprite_rects;
SpriteAnimationPlayback *anim_playbacks; Point camera_position;
SpriteAnimationPlayback* anim_playbacks;
int anim_playbacks_count; int anim_playbacks_count;
bool debug_mode;
} GameState; } GameState;
typedef struct { typedef struct {
@ -53,12 +57,12 @@ Assets Init() {
knight_anims[ANIM_KNIGHT_RUN] = knight_run; knight_anims[ANIM_KNIGHT_RUN] = knight_run;
knight_anims[ANIM_KNIGHT_ATTACK_SIDE1] = knight_attack_side1; knight_anims[ANIM_KNIGHT_ATTACK_SIDE1] = knight_attack_side1;
knight_anims[ANIM_KNIGHT_ATTACK_SIDE2] = knight_attack_side2; knight_anims[ANIM_KNIGHT_ATTACK_SIDE2] = knight_attack_side2;
knight_anims[ANIM_KNIGHT_ATTACK_TOP1 ] = knight_attack_front1; knight_anims[ANIM_KNIGHT_ATTACK_TOP1] = knight_attack_front1;
knight_anims[ANIM_KNIGHT_ATTACK_TOP2 ] = knight_attack_front2; knight_anims[ANIM_KNIGHT_ATTACK_TOP2] = knight_attack_front2;
knight_anims[ANIM_KNIGHT_ATTACK_BACK1] = knight_attack_back1; knight_anims[ANIM_KNIGHT_ATTACK_BACK1] = knight_attack_back1;
knight_anims[ANIM_KNIGHT_ATTACK_BACK2] = knight_attack_back2; knight_anims[ANIM_KNIGHT_ATTACK_BACK2] = knight_attack_back2;
Assets assets = { 0 }; Assets assets = {0};
assets.textures = malloc(sizeof(Texture2D) * TEXTURES_BUF_SIZE); assets.textures = malloc(sizeof(Texture2D) * TEXTURES_BUF_SIZE);
assets.textures[0] = LoadTexture("assets/Terrain/Ground/Tilemap_Flat.png"); assets.textures[0] = LoadTexture("assets/Terrain/Ground/Tilemap_Flat.png");
assets.textures[1] = assets.textures[1] =
@ -66,30 +70,49 @@ Assets Init() {
return assets; return assets;
} }
void Update(GameState *game, Camera2D cam, float dt) { void Update(GameState *game, Camera2D *cam, float dt) {
(void)cam;
TickSpriteAnimations(&knight_anims[0], game->anim_playbacks, 1); TickSpriteAnimations(&knight_anims[0], game->anim_playbacks, 1);
float movement_speed = 250.0f * dt; const float movement_speed = 250.0f * dt;
Vector2 input_vel = {0}; Vector2 input_vel = {0};
if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) { if (IsKeyDown(KEY_RIGHT)) {
input_vel.x = 1; input_vel.x = 1;
} }
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) { if (IsKeyDown(KEY_LEFT)) {
input_vel.x = -1; input_vel.x = -1;
} }
if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) { if (IsKeyDown(KEY_UP)) {
input_vel.y = -1; input_vel.y = -1;
} }
if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) { if (IsKeyDown(KEY_DOWN)) {
input_vel.y = 1; input_vel.y = 1;
} }
const float cam_move_speed = 1050.0f * dt;
Vector2 cam_vel = {0};
if (IsKeyDown(KEY_D)) {
cam_vel.x = -1;
}
if (IsKeyDown(KEY_A)) {
cam_vel.x = 1;
}
if (IsKeyDown(KEY_W)) {
cam_vel.y = 1;
}
if (IsKeyDown(KEY_S)) {
cam_vel.y = -1;
}
cam_vel = Vector2Normalize(cam_vel);
game->camera_position = Vector2Scale(cam_vel, cam_move_speed);
cam->offset = Vector2Add(cam->offset, game->camera_position);
if (IsKeyPressed(KEY_SPACE) && game->knight.state != KNIGHT_ATTACKING) { if (IsKeyPressed(KEY_SPACE) && game->knight.state != KNIGHT_ATTACKING) {
game->knight.state = KNIGHT_ATTACKING; game->knight.state = KNIGHT_ATTACKING;
game->anim_playbacks[0].anim_id = ANIM_KNIGHT_ATTACK_SIDE1; game->anim_playbacks[0].anim_id = ANIM_KNIGHT_ATTACK_SIDE1;
PlayAnimation(&game->anim_playbacks[0], game->anim_playbacks[0]); PlayAnimation(&game->anim_playbacks[0], game->anim_playbacks[0]);
} }
if (IsKeyPressed(KEY_F1)) {
game->debug_mode = !game->debug_mode;
}
if (game->knight.state == KNIGHT_ATTACKING) { if (game->knight.state == KNIGHT_ATTACKING) {
game->anim_playbacks[0].anim_id = ANIM_KNIGHT_ATTACK_SIDE1; game->anim_playbacks[0].anim_id = ANIM_KNIGHT_ATTACK_SIDE1;
if (game->anim_playbacks[0].is_finished) { if (game->anim_playbacks[0].is_finished) {
@ -112,10 +135,10 @@ void Update(GameState *game, Camera2D cam, float dt) {
} }
} }
void Draw(GameState *game, Assets assets, Camera2D cam, float dt) { void Draw(const GameState *game, Assets assets, Camera2D cam, float dt) {
(void)cam; (void)cam;
(void)dt; (void)dt;
ClearBackground((Color){ 100, 149, 237, 255 }); ClearBackground((Color){100, 149, 237, 255});
int size = 32; int size = 32;
int topx = 300; int topx = 300;
@ -137,6 +160,17 @@ void Draw(GameState *game, Assets assets, Camera2D cam, float dt) {
Vector2 pos = {32 * col + topx, 32 * row + topy}; Vector2 pos = {32 * col + topx, 32 * row + topy};
Rectangle src_rect = {32 * atlas_col, 32 * atlas_row, 32, 32}; Rectangle src_rect = {32 * atlas_col, 32 * atlas_row, 32, 32};
DrawTextureRec(assets.textures[0], src_rect, pos, WHITE); DrawTextureRec(assets.textures[0], src_rect, pos, WHITE);
#ifdef DEBUG_MODE_ENABLED
if (game->debug_mode) {
Rectangle debug_frame = {
pos.x,
pos.y,
src_rect.width,
src_rect.height,
};
DrawRectangleLinesEx(debug_frame, 0.5f, RED);
}
#endif
} }
} }
for (int i = 0; i < game->anim_playbacks_count; i++) { for (int i = 0; i < game->anim_playbacks_count; i++) {
@ -148,7 +182,19 @@ void Draw(GameState *game, Assets assets, Camera2D cam, float dt) {
if (game->knight.look_dir == DIR_LEFT) { if (game->knight.look_dir == DIR_LEFT) {
src_rect.width = -abs((int)src_rect.width); src_rect.width = -abs((int)src_rect.width);
} }
DrawTextureRec(assets.textures[1], src_rect, game->knight.position, WHITE); Vector2 pos = Vector2Subtract(game->knight.position, knight_origin);
DrawTextureRec(assets.textures[1], src_rect, pos, WHITE);
#ifdef DEBUG_MODE_ENABLED
if (game->debug_mode) {
Rectangle debug_frame = {
pos.x,
pos.y,
anim->src_rect.width,
anim->src_rect.height,
};
DrawRectangleLinesEx(debug_frame, 2.0f, RED);
}
#endif
} }
} }
@ -176,7 +222,6 @@ int main(void) {
// First one is idle // First one is idle
game.anim_playbacks[0] = (SpriteAnimationPlayback){0}; game.anim_playbacks[0] = (SpriteAnimationPlayback){0};
game.knight = (Knight){0}; game.knight = (Knight){0};
// state.anim_playbacks[0].pos = (Position){ knight_idle.src_rect.x, knight_idle.src_rect.y };
Assets assets = Init(); Assets assets = Init();
@ -186,7 +231,7 @@ int main(void) {
game.frame_count++; game.frame_count++;
float dt = GetFrameTime(); float dt = GetFrameTime();
Update(&game, cam, dt); Update(&game, &cam, dt);
BeginDrawing(); BeginDrawing();
{ {

21
tinyswords.todo Normal file
View File

@ -0,0 +1,21 @@
#-*- mode: org -*-
#+TODO: TODO(t) INPROGRESS(i) BUG(b) | DONE(d)
#+STARTUP: show2levels
* DONE Import assets and draw a sprite to the world
* DONE Layout a basic map with tiles
* DONE Extract Init/Update/Draw to own functions with state in a struct
* DONE Knight idle animation
* DONE Configure Makefile to handle deps and updates
* DONE Knight state to handle attacking
* DONE Animation flipping
* DONE Debug mode to show sprite boundaries
* DONE Sprite position needs to be set to the center
* DONE Movement keys to move the camera
* TODO Left click on knight to select, right click to move
* TODO Handle multiple knights on the map at the same time
* TODO Ability to attack an entity, like a dummy
* TODO State to handle attacking when you right click a target
* TODO Select multiple units and make them all move
* TODO Flocking/Steering behaviors so many knights move in formation
* TODO Steering behavior so knights surround a target with spacing