Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Generic Game Loop  (Read 17995 times)

0 Members and 1 Guest are viewing this topic.

minimidge

  • Newbie
  • *
  • Posts: 3
    • View Profile
Generic Game Loop
« 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/ . 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.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10190
    • View Profile
    • development blog
    • Email
Re: Generic Game Loop
« Reply #1 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. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Nightly Builds: https://www.nightlybuilds.ch/
——————————————————————
Dev Blog: https://dev.my-gate.net/
Thor: http://www.bromeon.ch/libraries/thor/

minimidge

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Generic Game Loop
« Reply #2 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.  :)

minimidge

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Generic Game Loop
« Reply #3 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. :)

Foaly

  • Sr. Member
  • ****
  • Posts: 453
    • View Profile
Re: Generic Game Loop
« Reply #4 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.
« Last Edit: December 19, 2012, 01:05:39 pm by Foaly »

mateandmetal

  • Full Member
  • ***
  • Posts: 171
  • The bird is the word
    • View Profile
    • my blog
Re: Generic Game Loop
« Reply #5 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. I´m currently using the Fix Your Timestep method. It works great.
- Mate (beverage) addict
- Heavy metal addict _lml
- SFML 2 addict
- My first (and free) game: BichingISH!

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10190
    • View Profile
    • development blog
    • Email
Re: Generic Game Loop
« Reply #6 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:
  • Don't use using namespace sf; it's only confusing and can lead to some strange compiler/linker errors.
  • Always use forward slashes for #include MSVC supports them perfectly and one can even set it as default in VS.
  • Either use a const unsigned int or cast the FPS count to unsigned int before passing it to setFramerateLimit, otherwise you'll end up with compiler warnings.
  • For float values you also need to specify the .0f for 0.
  • Passing the sf::Color(0, 0, 0) is in two ways unnecessary; 1) if you want black it's more descriptive to use sf::Color::Black, 2) window.clear() does use black as default color, so one does not need to pass it.
  • Tip: By the standard main() doesn't require a return call.
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();
    }
}
Official FAQ: https://www.sfml-dev.org/faq.php
Nightly Builds: https://www.nightlybuilds.ch/
——————————————————————
Dev Blog: https://dev.my-gate.net/
Thor: http://www.bromeon.ch/libraries/thor/

krzat

  • Full Member
  • ***
  • Posts: 107
    • View Profile
Re: Generic Game Loop
« Reply #7 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.
SFML.Utils - useful extensions for SFML.Net

trilavia

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Generic Game Loop
« Reply #8 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/

Cheers!