Refresh Rate Independent Animations and Game-Speed-Dependent Updates
Category: SDL Adventure Game
After porting the game to iOS, I encountered two issues:
- The animation implementation relied on the
render
function being called a fixed number of times per second, matching the screen refresh rate, due to theSDL_RENDERER_PRESENTVSYNC
renderer flag. The refresh rates differ between my 2014 MacBook Pro (60Hz) and iPhone (120Hz), causing animations to play twice as fast on an iPhone and iPad. - I did not account for the time elapsed between
update
invocations in the game-speed-dependent game loop I had implemented.
Animations
I resolved the animation issue by tracking the time when the animation started and computing the correct frame according to the delta between the animation start time and the current time using SDL_GetTicks
.
1
2
3
4
5
6
7
8
9
10
11
12
13
int delta = 0;
int clip_index = 0;
int ms_per_frame = 83; // at 12 FPS: 1 / 12 * 1000
if (animation->is_playing) {
delta = SDL_GetTicks() - animation->start_time;
clip_index = (delta / ms_per_frame) % animation->frames;
}
SDL_Rect *srcrect = &animation->sprite_clips[clip_index];
SDL_Rect dstrect = {position.x, position.y, clip->w, clip->h};
// Render to screen
SDL_RenderCopyEx(renderer, animation->image.texture, srcrect, &dstrect, 0,
NULL, animation->flip);
Fox Movement
I resolved the fox movement issue by updating the fox_update
implementation to consider the time elapsed between invocations and by multiplying the position deltas (fox walking and sliding) by the delta_time
value passed to each update
function.
Before:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void fox_update(Fox *fox, float delta_time) {
// ...
switch (fox->state) {
// ...
case WALKING:
// ...
fox->current_position = (SDL_FPoint){
.x = fox->current_position.x + fox->direction.x * vel,
.y = fox->current_position.y + fox->direction.y * vel,
};
break;
}
// ...
}
After:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void fox_update(Fox *fox, float delta_time) {
// ...
switch (fox->state) {
// ...
case WALKING:
// ...
fox->current_position = (SDL_FPoint){
.x = fox->current_position.x + fox->direction.x * vel * delta_time,
.y = fox->current_position.y + fox->direction.y * vel * delta_time,
};
break;
}
// ...
}