Reputation: 1386
I am having an issue with animation in SDL 2. I tried two possible solutions to cap frame rate: one using a constant sleep of 16ms (just for testing) and an other using a timer for more accurate frame capping (runGameLoop_computed game loop function). For testing purposes I am just drawing and moving a rectangle using the SDL_RenderFillRect function, but both game loop methods create jittering in the movement of the rectangle.
Do you have any idea about what is wrong here and the animation is not smooth?
The complete code is this:
#include <iostream>
#include <SDL.h>
#include <chrono>
#include <thread>
#include <math.h>
using namespace std;
//sdl window and window renderer pointers
static SDL_Window *gWindow;
static SDL_Renderer *windowRenderer;
//terminates the app when users closes the window
static bool exitAppFlag = false;
static int window_width = 1000;
static double position_x = 0; // current x position to draw the rectangle
static double delta_time = 0; // the time passed since last draw
//initilizes SDL and displays the application window
void initSDL()
{
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
}
else
{
printf( "SDL ok \n");
//Create window
gWindow = SDL_CreateWindow( "Tank Multiplayer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_width, 500, SDL_WINDOW_SHOWN);
if( gWindow == nullptr )
{
printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
}
else
{
//Create renderer for window
windowRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED);
//windowRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_SOFTWARE);
if(windowRenderer == nullptr )
{
printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
}
else
{
//Initialize renderer color
SDL_SetRenderDrawColor(windowRenderer, 0, 0, 255, 255);
//SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0x00 );
}
} //SDL_CreateWindow
}
}
//part of game loop: processes events (currently handles only window close event)
void processEvents()
{
SDL_Event p_event;
while (SDL_PollEvent(&p_event) != 0){
if (p_event.type == SDL_EventType::SDL_QUIT){
//if the user closed the window, then set the flag to true, so that we can exit the application
exitAppFlag = true;
return;
}
}
}
//part of game loop: updates the position_x variable based on the time passed since the last time (delta_time)
void update()
{
static double speed = 0.0532;
position_x += delta_time * speed;
if (position_x > window_width) position_x = 0;
}
//part of game loop: draws the rectange to the screen
void draw()
{
SDL_Rect r;
r.h = 300;
r.w = 100;
r.x = static_cast<int>(round(position_x));
r.y = 0;
SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0x00 );
SDL_RenderClear(windowRenderer);
SDL_SetRenderDrawColor(windowRenderer, 0, 0, 255, 255);
SDL_RenderFillRect(windowRenderer, &r);
SDL_RenderPresent(windowRenderer);
}
//game loop: frame capping based on the given fps
void runGameLoop_computed()
{
static double fps = 60;
static double single_frame_time_micro = (1000 / fps) * 1000;
std::chrono::time_point<std::chrono::high_resolution_clock>begin_time_point = std::chrono::high_resolution_clock::now();//stores the time point before processing game objects and drawing
long long delta_time_micro = 0;
while (!exitAppFlag) {
delta_time_micro = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - begin_time_point).count();
if (delta_time_micro < single_frame_time_micro){
std::this_thread::sleep_for(std::chrono::microseconds(static_cast<long long>(single_frame_time_micro - delta_time_micro)));
delta_time_micro = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - begin_time_point).count();
}
delta_time = delta_time_micro / 1000.00;
//std::cout << delta_time << std::endl;
begin_time_point = std::chrono::high_resolution_clock::now();//stores the time point before processing game objects and drawing
processEvents();
update();
draw();
}
}
//game loop: constant sleep time 16 ms, almost 60fps
void runGameLoop_static()
{
delta_time = 16.0;
while (!exitAppFlag) {
SDL_Delay(16);
processEvents();
update();
draw();
}
}
int main()
{
//initilize SDL library and create window
initSDL();
//enter game loop
//runGameLoop_static(); //constant sleep time 16ms
runGameLoop_computed(); //frame capping based on given fps
return 0;
}
Inside the main function you can uncomment witch method to test: runGameLoop_static or runGameLoop_computed
I uploaded the complete Qt project here (macOS) : https://www.sendspace.com/file/1p4oqq
Upvotes: 2
Views: 1026
Reputation: 219225
I'm not sure if this will solve the jittering problem (it might), but you can increase your accuracy of hitting your target 60 frames/sec by sleeping until a time point, as opposed to sleeping for a time duration. This makes your loop logic simpler, relieving you of trying to compute how long it takes to process each frame.
void
runGameLoop_computed()
{
using FrameDuration = std::chrono::duration<int, std::ratio<1, 60>>;
auto next_start = std::chrono::steady_clock::now() + FrameDuration{1};
while (!exitAppFlag)
{
processEvents();
update();
draw();
std::this_thread::sleep_until(next_start);
next_start += FrameDuration{1};
}
}
Upvotes: 1