Animation code
This commit is contained in:
parent
4264188a58
commit
6ca4f19b6b
78
animation_player.c
Normal file
78
animation_player.c
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include "animation_player.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <SDL2/SDL_rect.h>
|
||||||
|
#include <SDL2/SDL_timer.h>
|
||||||
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
26
animation_player.h
Normal file
26
animation_player.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <SDL2/SDL_render.h>
|
||||||
|
|
||||||
|
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);
|
64
main.c
Normal file
64
main.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include "player.h"
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_timer.h>
|
||||||
|
#include <SDL2/SDL_rect.h>
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <SDL2/SDL_video.h>
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
200
player.c
Normal file
200
player.c
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#include "player.h"
|
||||||
|
#include "SDL_keycode.h"
|
||||||
|
#include "SDL_render.h"
|
||||||
|
#include "animation_player.h"
|
||||||
|
#include "state_machine.h"
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
68
player.h
Normal file
68
player.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "animation_player.h"
|
||||||
|
#include "state_machine.h"
|
||||||
|
#include <SDL2/SDL_rect.h>
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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);
|
9
state_machine.c
Normal file
9
state_machine.c
Normal file
@ -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);
|
||||||
|
}
|
16
state_machine.h
Normal file
16
state_machine.h
Normal file
@ -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);
|
Loading…
Reference in New Issue
Block a user