1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
#include "player.h"
Player::Player(SDL_Renderer* renderer)
: renderer{renderer}
, sprites{
Animation{renderer, "rest.bmp", 256, 1.0, true},
Animation{renderer, "takeoff.bmp", 256, 0.3, false},
Animation{renderer, "flight.bmp", 256, 1.3, false},
Animation{renderer, "landing.bmp", 256, 0.3, false},
Animation{renderer, "walk.bmp", 256, 1.0, true},
Animation{renderer, "fall.bmp", 256, 1.0, true}
}
{
}
void Player::set_state(const States& s)
{
timestamp = Clock::now();
state = s;
if (state != States::FLIGHT && state != States::WALK)
{
speed.x = 0;
}
else if (state == States::WALK)
{
speed.x = backwards ? -150 : 150;
}
else if (state == States::FLIGHT)
{
speed.y = jump.y;
speed.x = backwards ? -jump.x : jump.x;
}
}
void Player::handle_keyboard()
{
const Uint8* kbstate{SDL_GetKeyboardState(nullptr)};
if (state == States::WALK && !kbstate[SDL_SCANCODE_RIGHT] &&
!kbstate[SDL_SCANCODE_LEFT])
{
set_state(States::REST);
}
if ((state == States::REST || state == States::WALK) &&
kbstate[SDL_SCANCODE_UP])
{
if (kbstate[SDL_SCANCODE_LEFT] || kbstate[SDL_SCANCODE_RIGHT])
{
jump.x = 200; // long jump
jump.y = -200;
}
else
{
jump.x = 50; // high jump
jump.y = -300;
}
set_state(States::TAKEOFF);
}
if (state == States::REST &&
(kbstate[SDL_SCANCODE_LEFT] || kbstate[SDL_SCANCODE_RIGHT]))
{
backwards = kbstate[SDL_SCANCODE_LEFT];
set_state(States::WALK);
}
}
const Animation& Player::sprite(const States& state)
{
return sprites.at(static_cast<int>(state));
}
void Player::update_state(const double dt, const Map& map)
{
// takeoff -> flight
if (state == States::TAKEOFF && sprite(state).animation_ended(timestamp))
{
set_state(States::FLIGHT);
}
// landing -> rest
if (state == States::LANDING && sprite(state).animation_ended(timestamp))
{
set_state(States::REST);
}
// put free falling sprite if no ground under the feet
if (state != States::FLIGHT &&
map.is_empty(pos.x / map.tile_size.w, pos.y / map.tile_size.h + 1))
{
set_state(States::FALL);
}
// prior to collision detection
pos.x += dt * speed.x;
// horizontal collision detection
if (!map.is_empty(pos.x / map.tile_size.w, pos.y / map.tile_size.h))
{
// snap the coorinate to the boundary of last free tile
int snap = std::round(pos.x / map.tile_size.w) * map.tile_size.w;
// be careful to snap to the left or to the right side of the free tile
pos.x = snap + (snap > pos.x ? 1 : -1);
// stop
speed.x = 0;
}
// prior to collision detection
pos.y += dt * speed.y;
// gravity
speed.y += dt * 300;
// vertical collision detection
if (!map.is_empty(pos.x / map.tile_size.w, pos.y / map.tile_size.h))
{
// snap the coorinate to the boundary of last free tile
int snap = std::round(pos.y / map.tile_size.h) * map.tile_size.h;
// be careful to snap to the top or the bottom side of the free tile
pos.y = snap + (snap > pos.y ? 1 : -1);
// stop
speed.y = 0;
if (state == States::FLIGHT || state == States::FALL)
{
set_state(States::LANDING);
}
}
}
void Player::draw()
{
const SDL_Rect src{sprite(state).rect(timestamp)};
const SDL_Rect dest{
static_cast<int>(pos.x) - sprite_size.w / 2,
static_cast<int>(pos.y) - sprite_size.h,
sprite_size.w,
sprite_size.h
};
SDL_RenderCopyEx(
renderer,
sprite(state).texture,
&src,
&dest,
0,
nullptr,
backwards ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE
);
}
|