forked from cirosantilli/cpp-cheat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
animation_user_control_inertia.c
223 lines (200 loc) · 7.1 KB
/
animation_user_control_inertia.c
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*Some basic inertia / friction on a single body.
Gives a strong sense of realism.
Makes you want to play around with two or more bodies and collision detection,
but that can only lead to a time consuming broken re-implementation of a subset
of Box2D... right?
*/
#include "common.h"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT WINDOW_WIDTH
#define WINDOW_CENTER_X (WINDOW_WIDTH / 2)
#define WINDOW_CENTER_Y (WINDOW_HEIGHT / 2)
#define WINDOW_HEIGHT WINDOW_WIDTH
#define RECT_WIDTH (WINDOW_WIDTH / 50)
#define RECT_HEIGHT RECT_WIDTH
#define MAX_RECT_POS (WINDOW_WIDTH - RECT_WIDTH)
/*
* Accelerations in meters per second.
*
* I like to think like this: how long will it take to cross the screen
* starting from speed 0 with this constant acceleration?
*
* Remember that:
*
* x = acc * t^2 / 2
*
* so if:
*
* - `acc = MAX_RECT_POS * 2.0` it takes 1 second to cross the screen
* - `acc = MAX_RECT_POS` it takes sqrt(2) seconds to cross the screen
* - `acc = MAX_RECT_POS / 2.0` it takes 2 seconds to cross the screen
**/
#define ACC_USER_METERS_PER_SECOND (MAX_RECT_POS)
#define ACC_FRICTION_METERS_PER_SECOND (MAX_RECT_POS / 2.5)
#define ACC_GRAVITY_METERS_PER_SECOND (MAX_RECT_POS / 1.5)
typedef struct {
double
x,
y,
speed_x,
speed_y
;
} PlayerState;
void init_state(
PlayerState *player_state,
double *user_acc_x,
double *user_acc_y,
unsigned int *last_time
) {
player_state->x = WINDOW_WIDTH / 2.0;
player_state->y = WINDOW_HEIGHT / 2.0;
player_state->speed_x = 0.0;
player_state->speed_y = 0.0;
/* Encode if the movement keys are initially pressed or not. */
*user_acc_x = 0.0;
*user_acc_y = 0.0;
*last_time = SDL_GetTicks();
common_fps_init();
}
double get_new_coord(double x, double step) {
x += step;
x = fmod(x, MAX_RECT_POS);
if (x < 0)
x = MAX_RECT_POS;
return x;
}
/* A constant force in the direction opposite to movement,
* or stop the movement if it is too slow.
* */
double apply_constant_friction(double speed, double dt) {
double dv;
dv = ACC_FRICTION_METERS_PER_SECOND * dt;
if (fabs(speed) < dv)
speed = 0.0;
else
speed -= copysign(dv, speed);
return speed;
}
int main(void) {
PlayerState player_state;
SDL_Event event;
SDL_Rect rect, rect_sun;
SDL_Renderer *renderer;
SDL_Window *window;
int quit = 0;
unsigned int current_time, last_time;
double dt, acc_x, acc_y, user_acc_x, user_acc_y;
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer);
SDL_SetWindowTitle(window, "arrow keys: accelerate | esc: reset | q: quit");
rect.w = RECT_WIDTH;
rect.h = RECT_HEIGHT;
rect_sun.w = RECT_WIDTH;
rect_sun.h = RECT_HEIGHT;
rect_sun.x = WINDOW_CENTER_X;
rect_sun.y = WINDOW_CENTER_Y;
main_loop:
init_state(&player_state, &user_acc_x, &user_acc_y, &last_time);
while (!quit) {
while (SDL_PollEvent(&event) == 1) {
if (event.type == SDL_QUIT) {
quit = 1;
} else if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_LEFT:
user_acc_x = -1;
break;
case SDLK_RIGHT:
user_acc_x = 1;
break;
case SDLK_UP:
user_acc_y = -1;
break;
case SDLK_DOWN:
user_acc_y = 1;
break;
case SDLK_ESCAPE:
goto main_loop;
case SDLK_q:
goto quit;
}
} else if (event.type == SDL_KEYUP) {
switch (event.key.keysym.sym) {
case SDLK_LEFT:
case SDLK_RIGHT:
user_acc_x = 0;
break;
case SDLK_UP:
case SDLK_DOWN:
user_acc_y = 0;
break;
}
}
}
current_time = SDL_GetTicks();
if (current_time != last_time) {
dt = (current_time - last_time) / 1000.0;
/* Player movement. */
acc_x = user_acc_x * ACC_USER_METERS_PER_SECOND;
acc_y = user_acc_y * ACC_USER_METERS_PER_SECOND;
/* Other forces. */
{
/* Constant gravity down. */
/*acc_y += ACC_GRAVITY_METERS_PER_SECOND;*/
/* Constant gravity to center of screen, the "sun". */
{
double dx = player_state.x - WINDOW_CENTER_X;
double dy = player_state.y - WINDOW_CENTER_Y;
double angle = atan2(dy, dx);
double hyp = hypot(dx, dy);
/* TODO Failed attempt at force proportional to inverse square of distance. */
/*double mod = hyp > 0.001 ? hyp : 1.0;*/
/*double mod2 = mod * mod;*/
acc_x -= cos(angle) * ACC_GRAVITY_METERS_PER_SECOND;
acc_y -= sin(angle) * ACC_GRAVITY_METERS_PER_SECOND;
}
}
/* Update speed. */
player_state.speed_x += dt * acc_x;
player_state.speed_y += dt * acc_y;
/*
Constant friction
- decelerates very quickly
- speed is limited to a maximum
Ideally this force should be added to the acceleration simply,
but we must consider the case where the object stops completely
because of it separately.
*/
/*player_state.speed_x = apply_constant_friction(player_state.speed_x, dt);*/
/*player_state.speed_y = apply_constant_friction(player_state.speed_y, dt);*/
/* Friction proportional to speed:
- decelerates very quickly
- speed is limited to a maximum
Does not feel very natural.
*/
/* player_state.speed_x *= 0.999; */
/* player_state.speed_y *= 0.999; */
/* Update position. */
player_state.x = get_new_coord(player_state.x, player_state.speed_x * dt);
player_state.y = get_new_coord(player_state.y, player_state.speed_y * dt);
/* Render. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
rect.x = player_state.x;
rect.y = player_state.y;
SDL_RenderFillRect(renderer, &rect);
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
SDL_RenderFillRect(renderer, &rect_sun);
SDL_RenderPresent(renderer);
/* Update time. */
last_time = current_time;
}
common_fps_update_and_print();
}
quit:
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}