SFML community forums

Help => General => Topic started by: minimidge on December 18, 2012, 10:52:31 pm

Title: Generic Game Loop
Post by: minimidge on December 18, 2012, 10:52:31 pm
Hello. I'm relatively new to SFML, but I've dabbled around with Allegro and game engines for awhile. I'm trying to create a basic fixed time-step loop for a game in SFML. I based the loop off a tutorial for Allegro. You can see the tutorial here: http://fixbyproximity.com/2011/08/2d-game-dev-part-4-1-timing-our-game-loop-2/ (http://fixbyproximity.com/2011/08/2d-game-dev-part-4-1-timing-our-game-loop-2/) . The sourcecode is below the video if you're interested.

Anyways, I want the game to act independent of FPS. I wrote a simple program below. I want the circle to take 5 seconds to go from the left side to the right side of the screen (It moves 1 pixel every 1/60th of a second).
#include <SFML\Graphics.hpp>
using namespace sf;

int main()
{
        const float FPS = 60.0f; //The desired FPS. (The number of updates each second).
        bool redraw = true;      //Do I redraw everything on the screen?

        RenderWindow window(VideoMode(300, 300, 32), "Hello");
        Clock clock;
        CircleShape circle(10.0f);
        circle.setOrigin(10.0f, 10.0f);
        circle.setPosition(0, 150.0f);

        Event ev;
        while (window.isOpen())
        {
                //Wait until 1/60th of a second has passed, then update everything.
                if (clock.getElapsedTime().asSeconds() >= 1.0f / FPS)
                {
                        redraw = true; //We're ready to redraw everything
                        circle.move(1.0f, 0);
                        clock.restart();
                }
                else //Sleep until next 1/60th of a second comes around
                {
                        Time sleepTime = seconds((1.0f / FPS) - clock.getElapsedTime().asSeconds());
                        sleep(sleepTime);
                }

                //Handle input
                while (window.pollEvent(ev))
                {
                        if (ev.type == Event::Closed) window.close();
                }

                //Draw stuff if ready
                if (redraw)
                {
                        redraw = false;
                        window.clear(Color(0, 0, 0));
                        window.draw(circle);
                        window.display();
                }
        }

        return 0;
}

Fraps reports 58-59 fps, and the circle takes 5 seconds to do its thing. If I remove the if(redraw) condition, Fraps reports around 110 fps, and the circle still takes 5 seconds (which is what I want).

My questions is: is the above code a good way to approach this? I'm just worried that I'll discover some flaw halfway into a game and have to change everything.
Title: Re: Generic Game Loop
Post by: eXpl0it3r on December 19, 2012, 12:54:46 am
Well you actually can do this much easier with SFML: window.setFramerateLimit(60);
But both your and the SFML version have the same problem (since they're actually quite identical in the implementation), that they use sleep (or sf::sleep which calls sleep) and sleep() isn't as accurate as one could want it to be, also there could be some process killing the whole performance and the sleep call wasn't long enough (or something like that). Anyways you won't get a fully fixed frame rate value, but then again you maybe don't need a fixed one.

In short: Use setFramerateLimit (or your implementation) and if you have any major troubles with it, look out for different methods. In most cases it's fine though. ;)
Title: Re: Generic Game Loop
Post by: minimidge on December 19, 2012, 03:02:27 am
I've heard about the issues using sleep before. By saying I won't get a "fully fixed" frame rate do you mean it simply won't be 100% accurate? With setFramerateLimit(60) I get ~62 fps. That's fine with me if it's slightly off.

If I understand correctly I can modify it like so:
#include <SFML\Graphics.hpp>
using namespace sf;

int main()
{
    const float FPS = 60.0f; //The desired FPS. (The number of updates each second).
    bool redraw = true;      //Do I redraw everything on the screen?

    RenderWindow window(VideoMode(300, 300, 32), "Hello");
    window.setFramerateLimit(FPS);
    Clock clock;
    CircleShape circle(10.0f);
    circle.setOrigin(10.0f, 10.0f);
    circle.setPosition(0, 150.0f);

    Event ev;
    while (window.isOpen())
    {
        //Wait until 1/60th of a second has passed, then update everything.
        if (clock.getElapsedTime().asSeconds() >= 1.0f / FPS)
        {
            redraw = true; //We're ready to redraw everything
            circle.move(1.0f, 0);
            clock.restart();
        }

        //Handle input
        while (window.pollEvent(ev))
        {
            if (ev.type == Event::Closed) window.close();
        }

        //Draw stuff if ready
        if (redraw)
        {
            redraw = false;
            window.clear(Color(0, 0, 0));
            window.draw(circle);
            window.display();
        }
    }

    return 0;
}
 

Can I remove the
if (clock.getElapsedTime().asSeconds() >= 1.0f / FPS)
condition and the clock entirely, or will that cause the circle's speed to be FPS-dependent? I'm trying to understand exactly how much setFramerateLimit() does for me automatically.

Thank you for your time.  :)
Title: Re: Generic Game Loop
Post by: minimidge on December 19, 2012, 03:12:45 am
Oops I just realized leaving that conditional in there causes the ball to move slower than it should. I took it out and now it acts normally. I guess I sort of answered my own question. :)
Title: Re: Generic Game Loop
Post by: Foaly on December 19, 2012, 01:03:14 pm
By saying I won't get a "fully fixed" frame rate do you mean it simply won't be 100% accurate? With setFramerateLimit(60) I get ~62 fps.
Yes that's what he meant.

Your code should look something like this:
#include <SFML\Graphics.hpp>
using namespace sf;

int main()
{
    const float FPS = 60.0f; //The desired FPS. (The number of updates each second).

    RenderWindow window(VideoMode(300, 300, 32), "Hello");
    window.setFramerateLimit(FPS);

    CircleShape circle(10.0f);
    circle.setOrigin(10.0f, 10.0f);
    circle.setPosition(0, 150.0f);

    Event ev;
    while (window.isOpen())
    {
        //Handle events
        while (window.pollEvent(ev))
        {
            if (ev.type == Event::Closed) window.close();
        }

        // Move stuff
        circle.move(1.0f, 0);

        // Draw stuff
        window.clear(Color(0, 0, 0));
        window.draw(circle);
        window.display();
    }

    return 0;
}

But I guess you already figured that out by yourself. Btw the sleep() call will happen inside the window.display() call, in case you're wondering.
Title: Re: Generic Game Loop
Post by: mateandmetal on December 19, 2012, 05:33:38 pm
Anyways, I want the game to act independent of FPS.

NEVER use setFramerateLimit() !!!  ;D

Please read my post here (http://en.sfml-dev.org/forums/index.php?topic=8996.msg60538#msg60538). I´m currently using the Fix Your Timestep (http://gafferongames.com/game-physics/fix-your-timestep/) method. It works great.
Title: Re: Generic Game Loop
Post by: eXpl0it3r on December 19, 2012, 06:27:21 pm
Quote
By saying I won't get a "fully fixed" frame rate do you mean it simply won't be 100% accurate?
Yes but also that you can get unwanted spikes that could lead to quite some strange behavior in the physics.

NEVER use setFramerateLimit() !!!  ;D
Don't exaggerate here. :P

Yes it's a good idea to use a fixed timestep, specially for physics, but for simple games where the CPU is mostly bored to death, it doesn't really matter. ;)

If one wants a more stable framerate vsync could be an option, although that can get deactivated by the driver settings.


Your code should look something like this:
A few things one should/could do different/better:
So that would turn into:

#include <SFML/Graphics.hpp>

int main()
{
    const unsigned int FPS = 60; //The desired FPS. (The number of updates each second).

    sf::RenderWindow window(sf::VideoMode(300, 300), "Hello");
    window.setFramerateLimit(FPS);

    sf::CircleShape circle(10.0f);
    circle.setOrigin(10.0f, 10.0f);
    circle.setPosition(0.0f, 150.0f);

    sf::Event ev;
    while (window.isOpen())
    {
        //Handle events
        while (window.pollEvent(ev))
        {
            if (ev.type == sf::Event::Closed)
                window.close();
        }

        // Move stuff
        circle.move(1.0f, 0.0f);

        // Draw stuff
        window.clear();
        window.draw(circle);
        window.display();
    }
}
Title: Re: Generic Game Loop
Post by: krzat on December 19, 2012, 08:17:39 pm
        if (clock.getElapsedTime().asSeconds() >= 1.0f / FPS)
        {
            redraw = true; //We're ready to redraw everything
            circle.move(1.0f, 0);
            clock.restart();
        }
 
Here is your problem. If you want really accurate FPS, you can't reset clock like that. You have to keep the error:
auto er = clock.getElapsedTime().asSeconds() - 1f / FPS;
 
And use it in your next frame.
For example, if you want 100 fps, and your clock shows 11 ms, your next frame has to last 9 ms, so it is 10 msecs on average.
Title: Re: Generic Game Loop
Post by: trilavia on December 19, 2012, 10:46:53 pm
Never do game loop which uses delta time for movement and never try to have a game that tries to run at 60 fps constantly... The only way to go is the fixed step with interpolation. Even if in simple 2d games it's not necessary, it's a good idea to have a good habbit since beggining, and when you move into 3d or even 2d fast paced games with physic, you will avoid A LOT of problems. The classic tutorial on this topic: http://gafferongames.com/game-physics/fix-your-timestep/ (http://gafferongames.com/game-physics/fix-your-timestep/)

Cheers!