From 6ca4f19b6b8c31cc5d8eb4c97f0f7882270ef5fd Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Mon, 4 Nov 2024 20:01:19 +0000 Subject: [PATCH] Animation code --- animation_player.c | 78 ++++++++++++++++++ animation_player.h | 26 ++++++ main.c | 64 +++++++++++++++ player.c | 200 +++++++++++++++++++++++++++++++++++++++++++++ player.h | 68 +++++++++++++++ state_machine.c | 9 ++ state_machine.h | 16 ++++ 7 files changed, 461 insertions(+) create mode 100644 animation_player.c create mode 100644 animation_player.h create mode 100644 main.c create mode 100644 player.c create mode 100644 player.h create mode 100644 state_machine.c create mode 100644 state_machine.h diff --git a/animation_player.c b/animation_player.c new file mode 100644 index 0000000..547eaa9 --- /dev/null +++ b/animation_player.c @@ -0,0 +1,78 @@ +#include "animation_player.h" +#include +#include +#include +#include +#include + +AnimPlayer ap_init(SDL_Renderer *renderer, const char *filepath, uint32_t ms_speed, uint32_t sprite_width, uint32_t sprite_height, bool loop) { + SDL_Texture *image = IMG_LoadTexture(renderer, filepath); + if (!image) { + return (AnimPlayer){0}; + } + + int w; + if (SDL_QueryTexture(image, NULL, NULL, &w, NULL) < 0) { + return (AnimPlayer){0}; + } + + AnimPlayer ap = { + .image = image, + .last_time = SDL_GetTicks(), + .ms_speed = ms_speed, + .sprite_width = sprite_width, + .sprite_height = sprite_height, + .count = w / sprite_width, + .current = 0, + .loop = loop, + .finished = false, + }; + + ap.ms_duration = ap.ms_speed * ap.count; + + return ap; +} + +void ap_update(AnimPlayer *ap, uint32_t ticks) { + if (!ap) { + return; + } + + if (ap->current + 1 == ap->count && !(ap->loop)) { + ap->finished = true; + return; + } + + if (ticks - ap->last_time >= ap->ms_speed) { + ap->last_time = ticks; + ap->current = ++ap->current % ap->count; + } +} + +void ap_draw(SDL_Renderer *renderer, const AnimPlayer *ap, const SDL_Rect *dst, bool x_flip) { + if (!renderer || !ap || !(ap->image)) { + return; + } + + SDL_Rect src = { + .x = ap->current * ap->sprite_width, + .y = 0, + .w = ap->sprite_width, + .h = ap->sprite_height, + }; + SDL_RenderCopyEx(renderer, ap->image, &src, dst, 0, NULL, x_flip ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE); +} + +void ap_reset(AnimPlayer *ap) { + ap->current = 0; + ap->finished = false; +} + +void ap_destroy(AnimPlayer *ap) { + if (!ap || !(ap->image)) { + return; + } + + SDL_DestroyTexture(ap->image); + ap->image = NULL; +} diff --git a/animation_player.h b/animation_player.h new file mode 100644 index 0000000..a2cee82 --- /dev/null +++ b/animation_player.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +typedef struct anim_player AnimPlayer; + +struct anim_player { + SDL_Texture *image; + uint32_t last_time; + uint32_t ms_speed; + uint32_t ms_duration; + uint32_t sprite_width; + uint32_t sprite_height; + uint16_t count; + uint16_t current; + bool loop; + bool finished; +}; + +AnimPlayer ap_init (SDL_Renderer *renderer, const char *filepath, uint32_t ms_speed, uint32_t sprite_width, uint32_t sprite_height, bool loop); +void ap_update (AnimPlayer *ap, uint32_t ticks); +void ap_draw (SDL_Renderer *renderer, const AnimPlayer *ap, const SDL_Rect *dst, bool x_flip); +void ap_reset (AnimPlayer *ap); +void ap_destroy(AnimPlayer *ap); diff --git a/main.c b/main.c new file mode 100644 index 0000000..3f1d6ca --- /dev/null +++ b/main.c @@ -0,0 +1,64 @@ +#include "player.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WINDOW_WIDTH 1280 +#define WINDOW_HEIGHT 720 + +int main() { + SDL_Init(SDL_INIT_EVERYTHING); + IMG_Init(IMG_INIT_PNG); + + SDL_Window *window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN); + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + + Player player = {0}; + player_init(&player, renderer, (SDL_Rect){ .x = 50, .y = 100, .w = 100, .h = 64 }); + + bool running = true; + + SDL_Event event = {0}; + uint32_t ticks; + + while (running) { + while (SDL_PollEvent(&event)) { + player_events(&player, &event); + + switch (event.type) { + case SDL_QUIT: + running = false; + break; + } + } + + ticks = SDL_GetTicks(); + + player_update(&player, ticks); + + SDL_SetRenderDrawColor(renderer, 0, 94, 124, 255); + + SDL_RenderClear(renderer); + + player_draw(&player, renderer); + + SDL_RenderPresent(renderer); + } + + SDL_DestroyRenderer(renderer); + + SDL_DestroyWindow(window); + + IMG_Quit(); + SDL_Quit(); + + return 0; +} diff --git a/player.c b/player.c new file mode 100644 index 0000000..571125f --- /dev/null +++ b/player.c @@ -0,0 +1,200 @@ +#include "player.h" +#include "SDL_keycode.h" +#include "SDL_render.h" +#include "animation_player.h" +#include "state_machine.h" +#include +#include +#include + +#define SPRITE_WIDTH 100 +#define SPRITE_HEIGHT 64 + +State *update_state(Player *player, uint32_t state); +State *idle_state(StateMachine *sm, Player *player); +State *walk_state(StateMachine *sm, Player *player); +State *jump_state(StateMachine *sm, Player *player); +State *fall_state(StateMachine *sm, Player *player); +State *dash_state(StateMachine *sm, Player *player); + +void player_init(Player *player, SDL_Renderer *renderer, SDL_Rect position) { + player->position = position; + player->movement = (Movement){0}; + player->x_direction = PLAYER_DIRECTION_RIGHT; + player->velocity = 4; + + player->animations[PLAYER_STATE_IDLE] = + ap_init(renderer, "knight_player/Idle_KG_2.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, true); + player->animations[PLAYER_STATE_WALK] = + ap_init(renderer, "knight_player/Walking_KG_2.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, true); + player->animations[PLAYER_STATE_JUMP] = + ap_init(renderer, "knight_player/Jump_KG_2.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, false); + player->animations[PLAYER_STATE_FALL] = + ap_init(renderer, "knight_player/Fall_KG_2.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, false); + player->animations[PLAYER_STATE_DASH] = + ap_init(renderer, "knight_player/Dashing_KG_1.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, false); + + player->states[PLAYER_STATE_IDLE] = (State){ .state_func = (StateFunc *)idle_state }; + player->states[PLAYER_STATE_WALK] = (State){ .state_func = (StateFunc *)walk_state }; + player->states[PLAYER_STATE_JUMP] = (State){ .state_func = (StateFunc *)jump_state }; + player->states[PLAYER_STATE_FALL] = (State){ .state_func = (StateFunc *)fall_state }; + player->states[PLAYER_STATE_DASH] = (State){ .state_func = (StateFunc *)dash_state }; + + player->event_data = (PlayerEventData){0}; + player->state_machine = (StateMachine){ .current_state = &(player->states[PLAYER_STATE_IDLE]) }; + player->current_state = PLAYER_STATE_IDLE; +} + +void player_events(Player *player, const SDL_Event *event) { + if (!player || !event) { + return; + } + + switch (event->type) { + case SDL_KEYDOWN: + switch (event->key.keysym.sym) { + case SDLK_LEFT: + player->movement.x = PLAYER_DIRECTION_LEFT * player->velocity; + player->x_direction = PLAYER_DIRECTION_LEFT; + player->event_data.key = PLAYER_CONTROL_LEFT_DOWN; + break; + case SDLK_RIGHT: + player->movement.x = PLAYER_DIRECTION_RIGHT * player->velocity; + player->x_direction = PLAYER_DIRECTION_RIGHT; + player->event_data.key = PLAYER_CONTROL_RIGHT_DOWN; + break; + } + break; + case SDL_KEYUP: + switch (event->key.keysym.sym) { + case SDLK_LEFT: + player->movement.x = 0; + player->event_data.key = PLAYER_CONTROL_LEFT_UP; + break; + case SDLK_RIGHT: + player->movement.x = 0; + player->event_data.key = PLAYER_CONTROL_RIGHT_UP; + break; + case SDLK_SPACE: + player->event_data.key = PLAYER_CONTROL_SPACE; + break; + case SDLK_LCTRL: + player->movement.x = player->x_direction * player->velocity * 3; + player->event_data.key = PLAYER_CONTROL_LCTRL; + + if (player->current_state == PLAYER_STATE_JUMP) { + player->event_data.dash_in_air = true; + } else { + player->event_data.dash_in_air = false; + } + + break; + } + break; + } +} + +void player_update(Player *player, uint32_t ticks) { + player->position.x += player->movement.x; + sm_run(&(player->state_machine), (void *)player); + ap_update(&(player->animations[player->current_state]), ticks); + player->event_data.key = PLAYER_CONTROL_NONE; +} + +void player_draw(const Player *player, SDL_Renderer *renderer) { + ap_draw(renderer, &(player->animations[player->current_state]), &(player->position), player->x_direction == PLAYER_DIRECTION_LEFT); +} + +State *update_state(Player *player, uint32_t state) { + if (state >= COUNT_PLAYER_STATES) { + return &(player->states[player->current_state]); + } + + player->current_state = state; + return &(player->states[state]); +} + +State *idle_state(StateMachine *sm, Player *player) { + switch (player->event_data.key) { + case PLAYER_CONTROL_LEFT_DOWN: + case PLAYER_CONTROL_RIGHT_DOWN: + return update_state(player, PLAYER_STATE_WALK); + case PLAYER_CONTROL_SPACE: + return update_state(player, PLAYER_STATE_JUMP); + } + + return update_state(player, PLAYER_STATE_IDLE); +} + +State *walk_state(StateMachine *sm, Player *player) { + switch (player->event_data.key) { + case PLAYER_CONTROL_LEFT_UP: + case PLAYER_CONTROL_RIGHT_UP: + return update_state(player, PLAYER_STATE_IDLE); + case PLAYER_CONTROL_SPACE: + return update_state(player, PLAYER_STATE_JUMP); + case PLAYER_CONTROL_LCTRL: + return update_state(player, PLAYER_STATE_DASH); + } + + return update_state(player, PLAYER_STATE_WALK); +} + +State *jump_state(StateMachine *sm, Player *player) { + bool reset = false; + uint32_t state = PLAYER_STATE_JUMP; + + if (player->animations[PLAYER_STATE_JUMP].finished) { + reset = true; + state = PLAYER_STATE_FALL; + } else if (player->event_data.key == PLAYER_CONTROL_LCTRL) { + reset = true; + state = PLAYER_STATE_DASH; + } + + if (reset) { + ap_reset(&(player->animations[PLAYER_STATE_JUMP])); + } + + return update_state(player, state); +} + +State *fall_state(StateMachine *sm, Player *player) { + bool reset = false; + uint32_t state = PLAYER_STATE_FALL; + + if (player->animations[PLAYER_STATE_FALL].finished) { + reset = true; + state = PLAYER_STATE_IDLE; + } + + if (reset) { + ap_reset(&(player->animations[PLAYER_STATE_FALL])); + } + + return update_state(player, state); +} + +State *dash_state(StateMachine *sm, Player *player) { + bool reset = false; + uint32_t state = PLAYER_STATE_DASH; + + player->movement.x += player->x_direction * 10; + + if (player->animations[PLAYER_STATE_DASH].finished) { + reset = true; + player->movement.x = 0; + + if (player->event_data.dash_in_air) { + state = PLAYER_STATE_FALL; + } else { + state = PLAYER_STATE_IDLE; + } + } + + if (reset) { + ap_reset(&(player->animations[PLAYER_STATE_DASH])); + } + + return update_state(player, state); +} diff --git a/player.h b/player.h new file mode 100644 index 0000000..b3ccfde --- /dev/null +++ b/player.h @@ -0,0 +1,68 @@ +#pragma once + +#include "animation_player.h" +#include "state_machine.h" +#include +#include +#include +#include +#include + +enum player_states { + PLAYER_STATE_IDLE, + PLAYER_STATE_WALK, + PLAYER_STATE_JUMP, + PLAYER_STATE_FALL, + PLAYER_STATE_DASH, + + COUNT_PLAYER_STATES, +}; + +enum player_controls { + PLAYER_CONTROL_NONE, + PLAYER_CONTROL_LEFT_DOWN, + PLAYER_CONTROL_RIGHT_DOWN, + PLAYER_CONTROL_LEFT_UP, + PLAYER_CONTROL_RIGHT_UP, + PLAYER_CONTROL_SPACE, + PLAYER_CONTROL_LCTRL, + + COUNT_PLAYER_CONTROLS, +}; + +enum player_direction { + PLAYER_DIRECTION_LEFT = -1, + PLAYER_DIRECTION_RIGHT = 1, + PLAYER_DIRECTION_UP = -1, + PLAYER_DIRECTION_DOWN = 1, +}; + +typedef struct player_event_data PlayerEventData; +struct player_event_data { + uint32_t key; + bool dash_in_air; +}; + +typedef struct movement Movement; +struct movement { + int32_t x; + int32_t y; +}; + +typedef struct player Player; +struct player { + uint32_t current_state; + uint32_t velocity; + int32_t x_direction; + Movement movement; + SDL_Rect position; + PlayerEventData event_data; + StateMachine state_machine; + State states[COUNT_PLAYER_STATES]; + AnimPlayer animations[COUNT_PLAYER_STATES]; +}; + +void player_init (Player *player, SDL_Renderer *renderer, SDL_Rect position); +void player_events(Player *player, const SDL_Event *event); +void player_update(Player *player, uint32_t ticks); +void player_draw (const Player *player, SDL_Renderer *renderer); diff --git a/state_machine.c b/state_machine.c new file mode 100644 index 0000000..6bb4b5c --- /dev/null +++ b/state_machine.c @@ -0,0 +1,9 @@ +#include "state_machine.h" + +void sm_run(StateMachine *sm, void *event_data) { + if (!sm || !(sm->current_state)) { + return; + } + + sm->current_state = sm->current_state->state_func(sm, event_data); +} diff --git a/state_machine.h b/state_machine.h new file mode 100644 index 0000000..84e62b8 --- /dev/null +++ b/state_machine.h @@ -0,0 +1,16 @@ +#pragma once + +typedef struct state State; +typedef struct state_machine StateMachine; +typedef State *(StateFunc)(StateMachine *sm, void *event_data); +typedef void NoEventData; + +struct state { + StateFunc *state_func; +}; + +struct state_machine { + State *current_state; +}; + +void sm_run(StateMachine *sm, void *event_data);