Animation code
This commit is contained in:
		
							
								
								
									
										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); | ||||
		Reference in New Issue
	
	Block a user