Add player jump and fall states
This commit is contained in:
		
							
								
								
									
										182
									
								
								player.c
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								player.c
									
									
									
									
									
								
							| @@ -1,21 +1,45 @@ | |||||||
| #include "player.h" | #include "player.h" | ||||||
| #include "SDL_keycode.h" |  | ||||||
| #include "SDL_render.h" |  | ||||||
| #include "animation_player.h" | #include "animation_player.h" | ||||||
| #include "state_machine.h" | #include "state_machine.h" | ||||||
| #include <SDL2/SDL_events.h> | #include <SDL2/SDL_events.h> | ||||||
|  | #include <SDL2/SDL_keyboard.h> | ||||||
|  | #include <SDL2/SDL_scancode.h> | ||||||
|  | #include <SDL2/SDL_render.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  |  | ||||||
| #define SPRITE_WIDTH 100 | #define SPRITE_WIDTH 100 | ||||||
| #define SPRITE_HEIGHT 64 | #define SPRITE_HEIGHT 64 | ||||||
|  |  | ||||||
| State *update_state(Player *player, uint32_t state); | void  print_state          (Player *player) { | ||||||
| State *idle_state(StateMachine *sm, Player *player); | 	switch(player->current_state) { | ||||||
| State *walk_state(StateMachine *sm, Player *player); | 		case PLAYER_STATE_IDLE: | ||||||
| State *dash_state(StateMachine *sm, Player *player); | 			printf("IDLE\n"); | ||||||
|  | 			break; | ||||||
|  | 		case PLAYER_STATE_WALK: | ||||||
|  | 			printf("WALK\n"); | ||||||
|  | 			break; | ||||||
|  | 		case PLAYER_STATE_DASH: | ||||||
|  | 			printf("DASH\n"); | ||||||
|  | 			break; | ||||||
|  | 		case PLAYER_STATE_JUMP: | ||||||
|  | 			printf("JUMP\n"); | ||||||
|  | 			break; | ||||||
|  | 		case PLAYER_STATE_FALL: | ||||||
|  | 			printf("FALL\n"); | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | void  get_player_controls(Player *player); | ||||||
|  | State *update_state      (Player *player, uint32_t state); | ||||||
|  | State *idle_state        (StateMachine *sm, Player *player); | ||||||
|  | State *walk_state        (StateMachine *sm, Player *player); | ||||||
|  | State *dash_state        (StateMachine *sm, Player *player); | ||||||
|  | State *jump_state        (StateMachine *sm, Player *player); | ||||||
|  | State *fall_state        (StateMachine *sm, Player *player); | ||||||
|  |  | ||||||
| void player_init(Player *player, SDL_Renderer *renderer, SDL_Rect position) { | void player_init(Player *player, SDL_Renderer *renderer, SDL_Rect position) { | ||||||
|  | 	player->base_y      = position.y; | ||||||
| 	player->position    = position; | 	player->position    = position; | ||||||
| 	player->movement    = (Movement){0}; | 	player->movement    = (Movement){0}; | ||||||
| 	player->x_direction = PLAYER_DIRECTION_RIGHT; | 	player->x_direction = PLAYER_DIRECTION_RIGHT; | ||||||
| @@ -27,27 +51,42 @@ void player_init(Player *player, SDL_Renderer *renderer, SDL_Rect position) { | |||||||
| 			ap_init(renderer, "knight_player/Walking_KG_2.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, true); | 			ap_init(renderer, "knight_player/Walking_KG_2.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, true); | ||||||
| 	player->animations[PLAYER_STATE_DASH] =  | 	player->animations[PLAYER_STATE_DASH] =  | ||||||
| 			ap_init(renderer, "knight_player/Dashing_KG_1.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, false); | 			ap_init(renderer, "knight_player/Dashing_KG_1.png", 125, SPRITE_WIDTH, SPRITE_HEIGHT, false); | ||||||
|  | 	player->animations[PLAYER_STATE_JUMP] =  | ||||||
|  | 			ap_init(renderer, "knight_player/Jump_KG_2.png",    100, SPRITE_WIDTH, SPRITE_HEIGHT, false); | ||||||
|  | 	player->animations[PLAYER_STATE_FALL] =  | ||||||
|  | 			ap_init(renderer, "knight_player/Fall_KG_2.png",     80, SPRITE_WIDTH, SPRITE_HEIGHT, false); | ||||||
|  |  | ||||||
| 	player->states[PLAYER_STATE_IDLE] = (State){ .state_func = (StateFunc *)idle_state }; | 	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_WALK] = (State){ .state_func = (StateFunc *)walk_state }; | ||||||
| 	player->states[PLAYER_STATE_DASH] = (State){ .state_func = (StateFunc *)dash_state }; | 	player->states[PLAYER_STATE_DASH] = (State){ .state_func = (StateFunc *)dash_state }; | ||||||
|  | 	player->states[PLAYER_STATE_JUMP] = (State){ .state_func = (StateFunc *)jump_state }; | ||||||
|  | 	player->states[PLAYER_STATE_FALL] = (State){ .state_func = (StateFunc *)fall_state }; | ||||||
|  |  | ||||||
| 	player->event_data    = (PlayerEventData){0}; |  | ||||||
| 	player->state_machine = (StateMachine){ .current_state = &(player->states[PLAYER_STATE_IDLE]) }; | 	player->state_machine = (StateMachine){ .current_state = &(player->states[PLAYER_STATE_IDLE]) }; | ||||||
| 	player->current_state = PLAYER_STATE_IDLE; | 	player->current_state = PLAYER_STATE_IDLE; | ||||||
| } | 	player->controls      = (PlayerControl){0}; | ||||||
|  |  | ||||||
| void player_events(Player *player, const SDL_Event *event) { |  | ||||||
| 	if (!player || !event) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	player->event_data.event = event; |  | ||||||
| 	sm_run(&(player->state_machine), (void *)player); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void player_update(Player *player, uint32_t ticks) { | void player_update(Player *player, uint32_t ticks) { | ||||||
|  | 	get_player_controls(player); | ||||||
|  |  | ||||||
|  | 	if (player->controls.walk_left) { | ||||||
|  | 		player->movement.x  = PLAYER_DIRECTION_LEFT * player->velocity; | ||||||
|  | 		player->x_direction = PLAYER_DIRECTION_LEFT; | ||||||
|  | 	} else if (player->controls.walk_right) { | ||||||
|  | 		player->movement.x  = PLAYER_DIRECTION_RIGHT * player->velocity; | ||||||
|  | 		player->x_direction = PLAYER_DIRECTION_RIGHT; | ||||||
|  | 	} else if (!(player->controls.walk_left) && !(player->controls.walk_right)) { | ||||||
|  | 		player->movement.x = 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sm_run(&(player->state_machine), (void *)player); | ||||||
|  |  | ||||||
| 	player->position.x += player->movement.x; | 	player->position.x += player->movement.x; | ||||||
|  | 	player->position.y += player->movement.y; | ||||||
|  | 	if (player->position.y > player->base_y) { | ||||||
|  | 		player->position.y = player->base_y; | ||||||
|  | 	} | ||||||
| 	ap_update(&(player->animations[player->current_state]), ticks); | 	ap_update(&(player->animations[player->current_state]), ticks); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -55,6 +94,21 @@ 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); | 	ap_draw(renderer, &(player->animations[player->current_state]), &(player->position), player->x_direction == PLAYER_DIRECTION_LEFT); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void get_player_controls(Player *player) { | ||||||
|  | 	const uint8_t *keys = SDL_GetKeyboardState(NULL); | ||||||
|  |  | ||||||
|  | 	player->controls.walk_left  = keys[SDL_SCANCODE_LEFT]  == SDL_PRESSED; | ||||||
|  | 	player->controls.walk_right = keys[SDL_SCANCODE_RIGHT] == SDL_PRESSED; | ||||||
|  |  | ||||||
|  | 	player->controls.space_last    = player->controls.space_pressed; | ||||||
|  | 	player->controls.space_pressed = keys[SDL_SCANCODE_SPACE] == SDL_PRESSED; | ||||||
|  | 	player->controls.jump          = player->controls.space_last && !(player->controls.space_pressed); | ||||||
|  |  | ||||||
|  | 	player->controls.lctrl_last    = player->controls.lctrl_pressed; | ||||||
|  | 	player->controls.lctrl_pressed = keys[SDL_SCANCODE_LCTRL] == SDL_PRESSED; | ||||||
|  | 	player->controls.dash          = player->controls.lctrl_last && !(player->controls.lctrl_pressed); | ||||||
|  | } | ||||||
|  |  | ||||||
| State *update_state(Player *player, uint32_t state) { | State *update_state(Player *player, uint32_t state) { | ||||||
| 	if (state >= COUNT_PLAYER_STATES) { | 	if (state >= COUNT_PLAYER_STATES) { | ||||||
| 		return &(player->states[player->current_state]); | 		return &(player->states[player->current_state]); | ||||||
| @@ -65,50 +119,34 @@ State *update_state(Player *player, uint32_t state) { | |||||||
| } | } | ||||||
|  |  | ||||||
| State *idle_state(StateMachine *sm, Player *player) { | State *idle_state(StateMachine *sm, Player *player) { | ||||||
| 	const SDL_Event *event = player->event_data.event; |  | ||||||
| 	uint32_t state = PLAYER_STATE_IDLE; | 	uint32_t state = PLAYER_STATE_IDLE; | ||||||
|  |  | ||||||
| 	switch (event->type) { | 	if (player->controls.jump) { | ||||||
| 		case SDL_KEYDOWN: | 		player->movement.y = player->velocity * 6 * -1; | ||||||
| 			switch (event->key.keysym.sym) { | 		state              = PLAYER_STATE_JUMP; | ||||||
| 				case SDLK_LEFT: | 	} else if (player->controls.walk_left) { | ||||||
| 					player->movement.x  = PLAYER_DIRECTION_LEFT * player->velocity; |  | ||||||
| 					player->x_direction = PLAYER_DIRECTION_LEFT; |  | ||||||
| 		state = PLAYER_STATE_WALK; | 		state = PLAYER_STATE_WALK; | ||||||
| 					break; | 	} else if (player->controls.walk_right) { | ||||||
| 				case SDLK_RIGHT: |  | ||||||
| 					player->movement.x  = PLAYER_DIRECTION_RIGHT * player->velocity; |  | ||||||
| 					player->x_direction = PLAYER_DIRECTION_RIGHT; |  | ||||||
| 		state = PLAYER_STATE_WALK; | 		state = PLAYER_STATE_WALK; | ||||||
| 					break; |  | ||||||
| 			} |  | ||||||
| 			break; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return update_state(player, state); | 	return update_state(player, state); | ||||||
| } | } | ||||||
|  |  | ||||||
| State *walk_state(StateMachine *sm, Player *player) { | State *walk_state(StateMachine *sm, Player *player) { | ||||||
| 	const SDL_Event *event = player->event_data.event; |  | ||||||
| 	uint32_t state         = PLAYER_STATE_WALK; | 	uint32_t state         = PLAYER_STATE_WALK; | ||||||
|  |  | ||||||
| 	switch (event->type) { | 	if (player->controls.jump) { | ||||||
| 		case SDL_KEYUP: | 		player->movement.y = player->velocity * 6 * -1; | ||||||
| 			switch (event->key.keysym.sym) { | 		state              = PLAYER_STATE_JUMP; | ||||||
| 				case SDLK_LCTRL: | 	} else if (player->controls.dash) { | ||||||
| 					player->movement.x = player->x_direction * player->velocity * 3; | 		player->controls.dash_in_air = false; | ||||||
|  | 		player->movement.x           = player->x_direction * player->velocity * 2; | ||||||
| 		state                        = PLAYER_STATE_DASH; | 		state                        = PLAYER_STATE_DASH; | ||||||
| 					break; |  | ||||||
| 				case SDLK_LEFT: |  | ||||||
| 					player->movement.x = 0; |  | ||||||
| 					state              = PLAYER_STATE_IDLE; |  | ||||||
| 					break; |  | ||||||
| 				case SDLK_RIGHT: |  | ||||||
| 					player->movement.x = 0; |  | ||||||
| 					state              = PLAYER_STATE_IDLE; |  | ||||||
| 					break; |  | ||||||
| 	} | 	} | ||||||
| 			break; |  | ||||||
|  | 	if (!(player->controls.walk_left) && !(player->controls.walk_right)) { | ||||||
|  | 		state = PLAYER_STATE_IDLE; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return update_state(player, state); | 	return update_state(player, state); | ||||||
| @@ -118,17 +156,65 @@ State *dash_state(StateMachine *sm, Player *player) { | |||||||
| 	bool reset     = false; | 	bool reset     = false; | ||||||
| 	uint32_t state = PLAYER_STATE_DASH; | 	uint32_t state = PLAYER_STATE_DASH; | ||||||
|  |  | ||||||
| 	player->movement.x += player->x_direction * 3; | 	player->movement.x += player->x_direction * 20; | ||||||
|  |  | ||||||
| 	if (player->animations[PLAYER_STATE_DASH].finished) { | 	if (player->animations[PLAYER_STATE_DASH].finished) { | ||||||
|  | 		if (player->controls.dash_in_air) { | ||||||
|  | 			player->movement.y = player->velocity * 6; | ||||||
|  | 			state              = PLAYER_STATE_FALL; | ||||||
|  | 		} else { | ||||||
| 			player->movement.x = 0; | 			player->movement.x = 0; | ||||||
| 		reset              = true; |  | ||||||
| 			state              = PLAYER_STATE_IDLE; | 			state              = PLAYER_STATE_IDLE; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		reset = true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (reset) { | 	if (reset) { | ||||||
| 		ap_reset(&(player->animations[PLAYER_STATE_DASH])); | 		ap_reset(&(player->animations[PLAYER_STATE_DASH])); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return update_state(player, state); | 	return update_state(player, state); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | State *jump_state(StateMachine *sm, Player *player) { | ||||||
|  | 	bool reset     = false; | ||||||
|  | 	uint32_t state = PLAYER_STATE_JUMP; | ||||||
|  |  | ||||||
|  | 	player->movement.y += player->velocity * 0.5; | ||||||
|  |  | ||||||
|  | 	if (player->controls.dash) { | ||||||
|  | 		player->controls.dash_in_air = true; | ||||||
|  | 		player->movement.x           = player->x_direction * player->velocity * 2; | ||||||
|  | 		player->movement.y           = player->velocity * 0.5; | ||||||
|  | 		reset                        = true; | ||||||
|  | 		state                        = PLAYER_STATE_DASH; | ||||||
|  | 	} else if (player->animations[PLAYER_STATE_JUMP].finished) { | ||||||
|  | 		player->movement.y = player->velocity * 6; | ||||||
|  | 		reset              = true; | ||||||
|  | 		state              = PLAYER_STATE_FALL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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) { | ||||||
|  | 		player->movement.y = 0; | ||||||
|  | 		reset              = true; | ||||||
|  | 		state              = PLAYER_STATE_IDLE; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (reset) { | ||||||
|  | 		ap_reset(&(player->animations[PLAYER_STATE_FALL])); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return update_state(player, state); | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								player.h
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								player.h
									
									
									
									
									
								
							| @@ -12,6 +12,8 @@ enum player_states { | |||||||
| 	PLAYER_STATE_IDLE, | 	PLAYER_STATE_IDLE, | ||||||
| 	PLAYER_STATE_WALK, | 	PLAYER_STATE_WALK, | ||||||
| 	PLAYER_STATE_DASH, | 	PLAYER_STATE_DASH, | ||||||
|  | 	PLAYER_STATE_JUMP, | ||||||
|  | 	PLAYER_STATE_FALL, | ||||||
|  |  | ||||||
| 	COUNT_PLAYER_STATES, | 	COUNT_PLAYER_STATES, | ||||||
| }; | }; | ||||||
| @@ -23,9 +25,17 @@ enum player_direction { | |||||||
| 	PLAYER_DIRECTION_DOWN  =  1, | 	PLAYER_DIRECTION_DOWN  =  1, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct player_event_data PlayerEventData; | typedef struct player_control PlayerControl; | ||||||
| struct player_event_data { | struct player_control { | ||||||
| 	const SDL_Event *event; | 	bool walk_left; | ||||||
|  | 	bool walk_right; | ||||||
|  | 	bool space_last; | ||||||
|  | 	bool space_pressed; | ||||||
|  | 	bool jump; | ||||||
|  | 	bool lctrl_last; | ||||||
|  | 	bool lctrl_pressed; | ||||||
|  | 	bool dash; | ||||||
|  | 	bool dash_in_air; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct movement Movement; | typedef struct movement Movement; | ||||||
| @@ -38,13 +48,14 @@ typedef struct player Player; | |||||||
| struct player { | struct player { | ||||||
| 	uint32_t        current_state;  | 	uint32_t        current_state;  | ||||||
| 	uint32_t        velocity;  | 	uint32_t        velocity;  | ||||||
|  | 	int32_t         base_y; | ||||||
| 	int32_t         x_direction; | 	int32_t         x_direction; | ||||||
| 	Movement        movement;  | 	Movement        movement;  | ||||||
| 	SDL_Rect        position; | 	SDL_Rect        position; | ||||||
| 	PlayerEventData event_data; |  | ||||||
| 	StateMachine    state_machine; | 	StateMachine    state_machine; | ||||||
| 	State           states[COUNT_PLAYER_STATES]; | 	State           states[COUNT_PLAYER_STATES]; | ||||||
| 	AnimPlayer      animations[COUNT_PLAYER_STATES]; | 	AnimPlayer      animations[COUNT_PLAYER_STATES]; | ||||||
|  | 	PlayerControl   controls; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void player_init  (Player *player, SDL_Renderer *renderer, SDL_Rect position); | void player_init  (Player *player, SDL_Renderer *renderer, SDL_Rect position); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user