|
Smooth animation when using fixed time step |
Sythical
Member #14,461
July 2012
|
Hello, I'm trying to implement the game loop where the physics is independent from rendering but my animation isn't as smooth as I would like and seems to be jumpy. Here is my code: 1// alpha is used for interpolation
2// counter_old_time is to do with displaying the fps
3double alpha = 0, counter_old_time = 0;
4double accumulator = 0, delta_time = 0, current_time = 0, previous_time = 0;
5unsigned frame_counter = 0, current_fps = 0; // also used for displaying the fps
6const unsigned physics_rate = 40, max_step_count = 5;
7
8// information about the magic ball (position and velocity)
9int old_pos_x = 100, new_pos_x = 100, render_pos_x = 100, velocity_x = 60;
10
11const double
12 step_duration = 1.0 / 40.0,
13 accumulator_max = step_duration * 5;
14
15previous_time = al_get_time();
16
17while(true) {
18 current_time = al_get_time();
19 delta_time = current_time - previous_time;
20 previous_time = current_time;
21 accumulator += delta_time;
22
23 if(accumulator > accumulator_max) {
24 accumulator = accumulator_max;
25 }
26
27 while(accumulator >= step_duration) {
28 if(new_pos_x > 1330) velocity_x = -15;
29 else if(new_pos_x < 70) velocity_x = 15;
30
31 old_pos_x = new_pos_x;
32 new_pos_x += velocity_x;
33 accumulator -= step_duration;
34 }
35
36 alpha = accumulator / static_cast<double>(step_duration);
37 render_pos_x = old_pos_x + (new_pos_x - old_pos_x) * alpha;
38
39 al_clear_to_color(al_map_rgb(20, 20, 40)); // clears the screen
40 al_draw_textf(font, al_map_rgb(255, 255, 255), 20, 20, 0, "current_fps: %i", current_fps); // print fps
41 al_draw_filled_circle(render_pos_x, 400, 15, al_map_rgb(255, 255, 255)); // draw circle
42 // I've added this to test how the program will behave when rendering takes
43 // considerably longer than updating the game.
44 al_rest(0.008);
45 al_flip_display(); // swaps the buffers
46
47 frame_counter++;
48
49 if(al_get_time() - counter_old_time >= 1) {
50 current_fps = frame_counter;
51 frame_counter = 0;
52 counter_old_time = al_get_time();
53 }
54}
On line 44, I've added a rest because I want to see how the code behaves when a lot of rendering is involved. If I remove it, the animation is smooth but I'm not happy with that since that's just hoping for the best. On my computer, the frame rate is around 125 and the animation is still jumpy. I've been trying to fix this for a week and have had no luck so I'd be very grateful if someone can help me. Thank you! Edit: I added the following code to work out the actual velocity (pixels per second) of the ball each time the ball is rendered and surprisingly it's not constant so I'm guessing that's the issue. I'm not sure why it's not constant. alpha = accumulator / static_cast<double>(step_duration); render_pos_x = old_pos_x + (new_pos_x - old_pos_x) * alpha; cout << (render_pos_x - old_render_pos) / delta_time << endl; old_render_pos = render_pos_x;
|
SiegeLord
Member #7,827
October 2006
|
I don't know the answer to your question... but instead I converted your example to the interpolation algorithm I tend to use. It seems to me that my way looks smoother, but that might be my brain lying to me. I didn't feel like setting up a double blind experiment to test . Also... it looks a lot smoother if you use vsync, at least on my computer. If you're on Linux, uncomment the al_wait_for_vsync line. If you're on Windows, uncomment al_set_new_display_option(ALLEGRO_VSYNC...) line. The fact that's its different is probably an Allegro bug. Here's your code (with added functions to make it run. I also slowed down the ball a bit.): 1#include <allegro5/allegro.h>
2#include <allegro5/allegro_primitives.h>
3#include <allegro5/allegro_font.h>
4
5int main()
6{
7 al_init();
8 al_init_primitives_addon();
9 al_init_font_addon();
10 auto font = al_create_builtin_font();
11
12 //al_set_new_display_option(ALLEGRO_VSYNC, ALLEGRO_REQUIRE, 1); // For Windows
13 auto d = al_create_display(800, 600);
14
15 // alpha is used for interpolation
16 // counter_old_time is to do with displaying the fps
17 double alpha = 0, counter_old_time = 0;
18 double accumulator = 0, delta_time = 0, current_time = 0, previous_time = 0;
19 unsigned frame_counter = 0, current_fps = 0; // also used for displaying the fps
20 const unsigned physics_rate = 40, max_step_count = 5;
21
22 // information about the magic ball (position and velocity)
23 int old_pos_x = 100, new_pos_x = 100, render_pos_x = 100, velocity_x = 10;
24
25 const double
26 step_duration = 1.0 / 40.0,
27 accumulator_max = step_duration * 5;
28
29 previous_time = al_get_time();
30
31 while(true) {
32 current_time = al_get_time();
33 delta_time = current_time - previous_time;
34 previous_time = current_time;
35 accumulator += delta_time;
36
37 if(accumulator > accumulator_max) {
38 accumulator = accumulator_max;
39 }
40
41 while(accumulator >= step_duration) {
42 if(new_pos_x > al_get_display_width(d) - 70) velocity_x = -10;
43 else if(new_pos_x < 70) velocity_x = 10;
44
45 old_pos_x = new_pos_x;
46 new_pos_x += velocity_x;
47 accumulator -= step_duration;
48 }
49
50 alpha = accumulator / static_cast<double>(step_duration);
51 render_pos_x = old_pos_x + (new_pos_x - old_pos_x) * alpha;
52
53 al_clear_to_color(al_map_rgb(20, 20, 40)); // clears the screen
54 al_draw_textf(font, al_map_rgb(255, 255, 255), 20, 20, 0, "current_fps: %i", current_fps); // print fps
55 al_draw_filled_circle(render_pos_x, 400, 15, al_map_rgb(255, 255, 255)); // draw circle
56 // I've added this to test how the program will behave when rendering takes
57 // considerably longer than updating the game.
58 //al_wait_for_vsync(); // For Linux
59 al_rest(0.008);
60 al_flip_display(); // swaps the buffers
61
62 frame_counter++;
63
64 if(al_get_time() - counter_old_time >= 1) {
65 current_fps = frame_counter;
66 frame_counter = 0;
67 counter_old_time = al_get_time();
68 }
69 }
70}
And here's mine: 1#include <allegro5/allegro.h>
2#include <allegro5/allegro_primitives.h>
3#include <allegro5/allegro_font.h>
4
5int main()
6{
7 al_init();
8 al_init_primitives_addon();
9 al_init_font_addon();
10 auto font = al_create_builtin_font();
11
12 //al_set_new_display_option(ALLEGRO_VSYNC, ALLEGRO_REQUIRE, 1); // For Windows
13 auto d = al_create_display(800, 600);
14
15 double counter_old_time = 0;
16 unsigned frame_counter = 0, current_fps = 0; // also used for displaying the fps
17 const unsigned physics_rate = 40, max_step_count = 5;
18 auto dt = 1.0 / physics_rate;
19
20 auto q = al_create_event_queue();
21 auto t = al_create_timer(dt);
22 al_register_event_source(q, al_get_timer_event_source(t));
23 al_start_timer(t);
24
25 // information about the magic ball (position and velocity)
26 int old_pos_x = 100, new_pos_x = 100, render_pos_x = 100, velocity_x = 10;
27
28 float game_time = 0;
29 float offset = al_get_time();
30
31 while(true) {
32 int steps = 0;
33 ALLEGRO_EVENT e;
34
35 while(al_get_next_event(q, &e)) {
36 if(e.type == ALLEGRO_EVENT_TIMER) {
37 game_time = e.timer.count * dt;
38
39 if(new_pos_x > al_get_display_width(d) - 70) velocity_x = -10;
40 else if(new_pos_x < 70) velocity_x = 10;
41
42 old_pos_x = new_pos_x;
43 new_pos_x += velocity_x;
44
45 steps++;
46
47 if(steps >= max_step_count)
48 break;
49 }
50 }
51
52 float alpha = (al_get_time() - offset - game_time) / dt;
53 render_pos_x = old_pos_x + (new_pos_x - old_pos_x) * alpha;
54
55 al_clear_to_color(al_map_rgb(20, 20, 40)); // clears the screen
56 al_draw_textf(font, al_map_rgb(255, 255, 255), 20, 20, 0, "current_fps: %i", current_fps); // print fps
57 al_draw_filled_circle(render_pos_x, 400, 15, al_map_rgb(255, 255, 255)); // draw circle
58 // I've added this to test how the program will behave when rendering takes
59 // considerably longer than updating the game.
60 //al_wait_for_vsync(); // For Linux
61 al_rest(0.008);
62 al_flip_display(); // swaps the buffers
63
64 frame_counter++;
65
66 if(al_get_time() - counter_old_time >= 1) {
67 current_fps = frame_counter;
68 frame_counter = 0;
69 counter_old_time = al_get_time();
70 }
71 }
72}
"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
Sythical
Member #14,461
July 2012
|
Hey SiegeLord, thank you for response. I tried both programs and they seem to be more or less identical on my computer. Vsync does make the animation a lot smoother so I'll start using that. I've read that vsync doesn't work in windowed mode because updates are managed by the OS and the game itself has no control over when the screen updates. I read this when I was trying to figure out how to enable vsync in SDL. I've been searching for an article which talks about frame rates and refresh rates with game development in mind. In the games I normally play (mainly WoW), the frame rate usually varies a lot but the gameplay is still butter smooth and if I lock the fps to something that doesn't match my monitor's refresh rate, so locked at 70 or 50, the gameplay is still smooth. In my game however, variable frame rate and not matching the refresh rate makes a huge difference. Can someone please briefly explain some of the things that I need to keep in mind? Thank you again |
SiegeLord
Member #7,827
October 2006
|
I don't see the point in drawing more often than the monitor refresh rate. Interpolation allows you to decouple your logic rate from the monitor refresh rate, while keeping things smooth. I have no trouble in enabling vsync in windowed applications on Linux or Windows. I don't know what technique WoW uses to handle things (it's almost certainly is not delta timing though, given that it works even when FPS drops to single digits). "For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
|