Compare commits

..

No commits in common. "ceca65fb46809796b915cd07876fe7575cc24295" and "b3cd3e65f72f7f16a128af89eeae0aff4aa3c495" have entirely different histories.

8 changed files with 93 additions and 229 deletions

1
.gitignore vendored
View File

@ -2,4 +2,3 @@
/deps/ /deps/
*.aseprite *.aseprite
/.idea/ /.idea/
/sprites.o

View File

@ -5,13 +5,10 @@ CC=gcc
all: main all: main
main: main.c sprites.o sprites.h game_data.h ./lib/libraylib.a main: main.c ./lib/libraylib.a
$(CC) $(CFLAGS) -Iinclude/ -lm main.c -o main sprites.o ./lib/libraylib.a $(CC) $(CFLAGS) -Iinclude/ -lm main.c -o main ./lib/libraylib.a
sprites.o: sprites.c sprites.h lib.h run: main
$(CC) $(CFLAGS) -c sprites.c -o sprites.o
run: all
./main ./main
clean: clean:

View File

@ -1,5 +1,3 @@
#pragma once
#define ANIM_KNIGHT_IDLE 0 #define ANIM_KNIGHT_IDLE 0
#define ANIM_KNIGHT_RUN 1 #define ANIM_KNIGHT_RUN 1
#define ANIM_KNIGHT_ATTACK_SIDE1 2 #define ANIM_KNIGHT_ATTACK_SIDE1 2
@ -9,7 +7,6 @@
#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},
@ -17,70 +14,53 @@ SpriteSheet knight = {
.size = { 1152, 1536 }, .size = { 1152, 1536 },
}; };
const Vector2 knight_origin = {cell_size / 2, cell_size / 2}; const u16 cell_size = 192;
SpriteAnimation knight_idle = { SpriteAnimation knight_idle = {
.name = "idle", .name = "idle",
.total_frames = 6, .total_frames = 6,
.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, .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, .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, .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, .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, .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, .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, .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];
// knight_anims[ANIM_KNIGHT_IDLE] = knight_idle;
// knight_anims[ANIM_KNIGHT_RUN] = knight_run;
// knight_anims[ANIM_KNIGHT_ATTACK_SIDE1] = knight_attack_side1;
// knight_anims[ANIM_KNIGHT_ATTACK_SIDE2] = knight_attack_side2;
// knight_anims[ANIM_KNIGHT_ATTACK_TOP1] = knight_attack_front1;
// knight_anims[ANIM_KNIGHT_ATTACK_TOP2] = knight_attack_front2;
// knight_anims[ANIM_KNIGHT_ATTACK_BACK1] = knight_attack_back1;
// knight_anims[ANIM_KNIGHT_ATTACK_BACK2] = knight_attack_back2;

9
lib.h
View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "include/raylib.h"
typedef uint8_t u8; typedef uint8_t u8;
// typedef char16_t c16; // typedef char16_t c16;
@ -18,15 +17,15 @@ typedef char byte;
typedef ptrdiff_t size; typedef ptrdiff_t size;
typedef size_t usize; typedef size_t usize;
typedef Vector2 Point; typedef Vector2 Position;
typedef struct Size { typedef struct Size {
float width; float width; // Rectangle width
float height; float height; // Rectangle height
} Size; } Size;
typedef struct Rect { typedef struct Rect {
Point position; Position position;
Size size; Size size;
} Rect; } Rect;

164
main.c
View File

@ -4,48 +4,22 @@
#include <stdbool.h> #include <stdbool.h>
#define TEXTURES_BUF_SIZE 16 #define TEXTURES_BUF_SIZE 16
#define TARGET_FPS 60 #define TARGET_FPS 120
#define ANIM_SPEED 8 #define ANIM_SPEED 8
#define MAX_ANIMATION_PLAYBACKS 64 #define MAX_ANIMATION_PLAYBACKS 64
#include "sprites.h" #include "sprites.h"
#include "game_data.h" #include "lib.h"
#include "config.h"
#define DEBUG_MODE_ENABLED
typedef enum KnightState {
KNIGHT_IDLE = 0,
KNIGHT_RUNNING = 1,
KNIGHT_ATTACKING = 2,
} KnightState;
typedef enum Direction {
DIR_UP = 0,
DIR_DOWN = 1,
DIR_LEFT = 2,
DIR_RIGHT = 3,
// DIR_UP_LEFT = 4,
// DIR_UP_RIGHT = 5,
// DIR_DOWN_LEFT = 6,
// DIR_DOWN_RIGHT = 7,
} Direction;
typedef struct {
Point position;
Point origin;
Vector2 velocity;
Direction look_dir;
KnightState state;
} Knight;
typedef struct { typedef struct {
int frame_count; int frame_count;
Knight knight; int anim_frames;
// Rectangle* sprite_rects; bool is_attacking;
Point camera_position; Vector2 knight_pos;
Rectangle *sprite_rects;
SpriteAnimationPlayback *anim_playbacks; SpriteAnimationPlayback *anim_playbacks;
int anim_playbacks_count; int anim_playbacks_count;
bool debug_mode;
} GameState; } GameState;
typedef struct { typedef struct {
@ -70,72 +44,48 @@ Assets Init() {
return assets; return assets;
} }
void Update(GameState *game, Camera2D *cam, float dt) { void Update(GameState *state, Camera2D cam, float dt) {
(void)cam;
TickSpriteAnimations(&knight_anims[0], game->anim_playbacks, 1); TickSpriteAnimations(&knight_anims[0], state->anim_playbacks, 1);
const float movement_speed = 250.0f * dt; float movement_speed = 250.0f * dt;
Vector2 input_vel = {0}; Vector2 input_vel = {0};
if (IsKeyDown(KEY_RIGHT)) { if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) {
input_vel.x = 1; input_vel.x = 1;
} }
if (IsKeyDown(KEY_LEFT)) { if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) {
input_vel.x = -1; input_vel.x = -1;
} }
if (IsKeyDown(KEY_UP)) { if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) {
input_vel.y = -1; input_vel.y = -1;
} }
if (IsKeyDown(KEY_DOWN)) { if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) {
input_vel.y = 1; input_vel.y = 1;
} }
const float cam_move_speed = 1050.0f * dt; if (IsKeyPressed(KEY_SPACE)) {
Vector2 cam_vel = {0}; state->is_attacking = true;
if (IsKeyDown(KEY_D)) {
cam_vel.x = -1;
} }
if (IsKeyDown(KEY_A)) { input_vel = Vector2Normalize(input_vel);
cam_vel.x = 1; input_vel = Vector2Scale(input_vel, movement_speed);
} state->knight_pos = Vector2Add(state->knight_pos, input_vel);
if (IsKeyDown(KEY_W)) { // if (state->is_attacking) {
cam_vel.y = 1; // if (state->anim_frames - attacking_frames > 5) {
} // state->is_attacking = false;
if (IsKeyDown(KEY_S)) { // state->knight_rect.y = 0;
cam_vel.y = -1; // } else {
} // state->knight_rect.y = 192 * 2;
cam_vel = Vector2Normalize(cam_vel); // }
game->camera_position = Vector2Scale(cam_vel, cam_move_speed); // } else {
cam->offset = Vector2Add(cam->offset, game->camera_position);
if (IsKeyPressed(KEY_SPACE) && game->knight.state != KNIGHT_ATTACKING) {
game->knight.state = KNIGHT_ATTACKING;
game->anim_playbacks[0].anim_id = ANIM_KNIGHT_ATTACK_SIDE1;
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) {
game->anim_playbacks[0].anim_id = ANIM_KNIGHT_ATTACK_SIDE1;
if (game->anim_playbacks[0].is_finished) {
game->knight.state = KNIGHT_IDLE;
}
} else {
if (input_vel.x != 0) {
game->knight.look_dir = input_vel.x == -1 ? DIR_LEFT : DIR_RIGHT;
}
game->knight.velocity = Vector2Normalize(input_vel);
game->knight.velocity = Vector2Scale(game->knight.velocity, movement_speed);
game->knight.position = Vector2Add(game->knight.position, game->knight.velocity);
if (input_vel.x != 0 || input_vel.y != 0) { if (input_vel.x != 0 || input_vel.y != 0) {
game->anim_playbacks[0].anim_id = ANIM_KNIGHT_RUN; state->anim_playbacks[0].anim_id = ANIM_KNIGHT_RUN;
game->knight.state = KNIGHT_RUNNING;
} else { } else {
game->anim_playbacks[0].anim_id = ANIM_KNIGHT_IDLE; state->anim_playbacks[0].anim_id = ANIM_KNIGHT_IDLE;
game->knight.state = KNIGHT_IDLE;
}
} }
// }
} }
void Draw(const GameState *game, Assets assets, Camera2D cam, float dt) { void Draw(GameState *state, 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 });
@ -160,41 +110,15 @@ void Draw(const 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 < state->anim_playbacks_count; i++) {
SpriteAnimationPlayback *playback = &game->anim_playbacks[i]; SpriteAnimationPlayback *playback = &state->anim_playbacks[i];
SpriteAnimation *anim = &knight_anims[playback->anim_id]; SpriteAnimation *anim = &knight_anims[playback->anim_id];
Rectangle src_rect = anim->src_rect; Rectangle src_rect = anim->src_rect;
src_rect.x = playback->current_frame * anim->src_rect.width; src_rect.x = playback->current_frame * anim->src_rect.width;
src_rect.y = anim->src_rect.y; src_rect.y = anim->src_rect.y;
if (game->knight.look_dir == DIR_LEFT) { DrawTextureRec(assets.textures[1], src_rect, state->knight_pos, WHITE);
src_rect.width = -abs((int)src_rect.width);
}
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
} }
} }
@ -216,28 +140,28 @@ int main(void) {
Camera2D cam = {0}; Camera2D cam = {0};
cam.zoom = 1.0f; cam.zoom = 1.0f;
GameState game = {0}; GameState state = {0};
game.anim_playbacks = malloc(sizeof(SpriteAnimationPlayback) * MAX_ANIMATION_PLAYBACKS); state.anim_playbacks = malloc(sizeof(SpriteAnimationPlayback) * MAX_ANIMATION_PLAYBACKS);
game.anim_playbacks_count = 1; state.anim_playbacks_count = 1;
// First one is idle // First one is idle
game.anim_playbacks[0] = (SpriteAnimationPlayback){0}; state.anim_playbacks[0] = (SpriteAnimationPlayback){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();
PlayAnimation(&game.anim_playbacks[0], game.anim_playbacks[0]); PlayAnimation(&state.anim_playbacks[0], state.anim_playbacks[0]);
// const int idle // const int idle
while (!WindowShouldClose()) { while (!WindowShouldClose()) {
game.frame_count++; state.frame_count++;
float dt = GetFrameTime(); float dt = GetFrameTime();
Update(&game, &cam, dt); Update(&state, cam, dt);
BeginDrawing(); BeginDrawing();
{ {
BeginMode2D(cam); BeginMode2D(cam);
{ {
Draw(&game, assets, cam, dt); Draw(&state, assets, cam, dt);
} }
EndMode2D(); EndMode2D();
} }

View File

@ -1,32 +0,0 @@
#include "sprites.h"
#include "include/raylib.h"
void PlayAnimation(SpriteAnimationPlayback *playbacks, SpriteAnimationPlayback playback) {
playback.time_elapsed = 0.0f;
playback.current_frame = 0;
playback.is_finished = false;
playbacks[0] = playback;
}
void TickSpriteAnimations(const SpriteAnimation *animations,
SpriteAnimationPlayback *playbacks,
int len) {
const f32 anim_speed = 1.0f / 10.0f;
for (int i = 0; i < len; i++) {
SpriteAnimationPlayback *playback = &playbacks[i];
const SpriteAnimation *animation = &animations[playback->anim_id];
playback->time_elapsed += GetFrameTime();
if (playback->time_elapsed >= anim_speed) {
playback->time_elapsed = 0.0f;
playback->current_frame++;
if (playback->current_frame >= animation->total_frames) {
if (animation->loop) {
playback->current_frame = 0;
} else {
playback->is_finished = true;
playback->current_frame--;
}
}
}
}
}

View File

@ -1,8 +1,7 @@
// sprites.h
#pragma once #pragma once
#include "lib.h" #include "lib.h"
#include "include/raylib.h" #include "raylib.h"
typedef struct { typedef struct {
Texture2D texture; Texture2D texture;
@ -12,17 +11,36 @@ typedef struct {
typedef struct { typedef struct {
char* name; char* name;
u8 total_frames; u16 total_frames;
bool loop;
Rectangle src_rect; Rectangle src_rect;
} SpriteAnimation; } SpriteAnimation;
typedef struct { typedef struct {
u16 anim_id; u16 anim_id;
u8 current_frame; u16 current_frame;
bool is_finished;
f32 time_elapsed; f32 time_elapsed;
} SpriteAnimationPlayback; } SpriteAnimationPlayback;
void PlayAnimation(SpriteAnimationPlayback *playbacks, SpriteAnimationPlayback playback); void PlayAnimation(SpriteAnimationPlayback *playbacks, SpriteAnimationPlayback playback) {
void TickSpriteAnimations(const SpriteAnimation *animations, SpriteAnimationPlayback *playbacks, int len); playback.time_elapsed = 0.0f;
playback.current_frame = 0;
playbacks[0] = playback;
}
void TickSpriteAnimations(SpriteAnimation *animations,
SpriteAnimationPlayback *playbacks,
int len) {
const f32 anim_speed = 1.0f / 10.0f;
for (int i = 0; i < len; i++) {
SpriteAnimationPlayback *playback = &playbacks[i];
SpriteAnimation *animation = &animations[playback->anim_id];
playback->time_elapsed += GetFrameTime();
if (playback->time_elapsed >= anim_speed) {
playback->time_elapsed = 0.0f;
playback->current_frame++;
if (playback->current_frame >= animation->total_frames) {
playback->current_frame = 0;
}
}
}
}

View File

@ -1,21 +0,0 @@
#-*- 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