SFML - How To Control Animation Speed

Pages: 12
I can't show you by modifying the code.

Let me ask you a question. Let's say that you have right arrow key constantly depressed. That would make the program repeatedly execute player.move(distance,0) statement.

Tell me, how much time does elapse between two calls to player.move() ? Is it always the same time interval? Is it sometimes a lot of time, sometimes a little? If there is some variation in elapsed time, what would be causing that variation?

Edit: please, also post your latest code.
Last edited on
As far as I'm aware, the amount of time that elapses between each accepted key press is at least one sixth of a second, which is what timePerFrame is set to. If there is variation in the time that elapses, it would only be a few milliseconds I would think, due to elapsedTime having to be greater than or equal to timePerFrame. However, I don't think that's what's causing the jagged, slightly jumpy movement at higher speeds.

This is the latest code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <iostream>
#include <SFML/Graphics.hpp>

int main()
{
    const int velocity = 50;

    // Six sprite frames will be displayed per second
    const float timePerFrame = 1.0 / 6.0;

    // Variables to store individual sprite dimensions
    int pixelWidth = 0;
    int pixelHeight = 0;

    // Offset is incremented to determine what rectangle of the spritesheet is being displayed
    int offset = 0;

    // Render a window
    sf::RenderWindow window(sf::VideoMode(960, 540), "Window");

    // Instantiate Texture object to hold spritesheet
    sf::Texture spritesheet;

    // Instantiate Sprite object to hold texture
    sf::Sprite player;

    // Instantiate Time object to store elapsed time between frames,
    sf::Time elapsedTime;

    // Instantiate Clock object to keep track of time passed
    sf::Clock clock;

    // Load spritesheet onto texture
    if(!spritesheet.loadFromFile("player.png"))
    {
        std::cout << "player.png could not be loaded." << std::endl;
    }
    else
    {
        // Apply loaded texture onto sprite
        player.setTexture(spritesheet);

        // Set sprite dimensions
        pixelWidth = 32;
        pixelHeight = 32;

        // Set players starting position
        player.setTextureRect(sf::IntRect(pixelWidth, pixelHeight * 0, pixelWidth, pixelHeight));
    }

    // Start main game loop (each loop is a full frame)
    while(window.isOpen())// Beginning of new frame
    {
        // elapsedTime will accumulate the time that has passed so far
        elapsedTime = clock.getElapsedTime();

        // Instantiate Event object to process window events (closing, resizing...)
        sf::Event event;

        // While the window processes events
        while(window.pollEvent(event))
        {
            // Close the window
            if(event.type == sf::Event::Closed)
            {
                window.close();
            }

            if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
            {
                window.close();
            }
        }

        // After the window has processed its events

        if(elapsedTime.asSeconds() >= timePerFrame)
        {
            if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
            {
                player.setTextureRect(sf::IntRect(pixelWidth * offset, pixelHeight * 3, pixelWidth, pixelHeight));

                player.move(0, -1 * elapsedTime.asSeconds() * velocity);

                offset++;
            }
            else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            {
                player.setTextureRect(sf::IntRect(pixelWidth * offset, pixelHeight * 0, pixelWidth, pixelHeight));

                player.move(0, 1 * elapsedTime.asSeconds() * velocity);

                offset++;
            }
            else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
            {
                player.setTextureRect(sf::IntRect(pixelWidth * offset, pixelHeight * 1, pixelWidth, pixelHeight));

                player.move(-1 * elapsedTime.asSeconds() * velocity, 0);

                offset++;
            }
            else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            {
                player.setTextureRect(sf::IntRect(pixelWidth * offset, pixelHeight * 2, pixelWidth, pixelHeight));

                player.move(1 * elapsedTime.asSeconds() * velocity, 0);

                offset++;
            }

            if(offset >= 3)
            {
                offset = 0;
            }

            // Reset elapsedTime
            elapsedTime = clock.restart();
        }

        // Clear the window with the colour black
        window.clear(sf::Color::Black);

        // Draw the player to the screen
        window.draw(player);

        // Display the newly updated screen
        window.display();
    }// End of current frame

    return 0;
}

Do you have SFML installed? If so and you would like to run the program to see what it's like right now, then you can google famitsu generator (I tried to link the URL but for some reason it sends you to a different page), download a generic spritesheet like I'm using and load that into the program. Seeing how the program works in action might give you a better idea of my problem.
With the code you posted above, the player movement would certainly be jumpy since the player position is updated no more often than 6 times per second, which is too low. To get smooth animation, you need at least 30 fps.

The second question is whether the program gets stuck in while(window.pollEvent(event)) loop for too long. But this is a secondary problem, you first have to solve the 6 fps issue.

Edit: I don't have SFML installed.
Last edited on
Can I just suggest another method of making frame rate independent movement animation which I believe ( I may be mistaken) is better because it copes with all speeds, not just too fast. If you store a floating point frame counter, which is incremented by the amount of time since last frame multiplied by a constant frame speed, then it will move smoothly (round down to pick a frame) and you should also move by movement speed * time rather than moving a certain amount every time more than a certain amount of time has passed.
@Kevin C
The timePerFrame variable isn't intended to control the overall framerate, only the rate at which the sprite cycles through its animations. It's when velocity is increased that the movement becomes jumpy.

I changed the while to an if and there's no difference.

@shadowmouse
Do you mean to do something like this:
frameCounter = frameCounter + (elapsedTime * timePerFrame);
If so, how would I implement it?

you should also move by movement speed * time
You mean like this? player.move(0, -elapsedTime.asSeconds() * velocity);
Almost. Simply have a floating point (double is almost always preferable to float) frame counter, which you round using std::floor() and then use it instead of offset. Also it should be elapsed time * animationSpeed, not timePerFrame as you want animationSpeed to be bigger if it is going faster, rather than smaller as with timePerFrame. In fact if you use this method you don't need timePerFrame because it isn't fixed frame rate it is frame rate independent. And for the movement that is exactly what I mean. Although I have noticed that you should use elapsedTime.asSeconds() both times, not just for movement.

Final point, you set elapsed time twice. Set it to clock.restart() at the start of the game loop and get rid of the other statement.
The timePerFrame variable isn't intended to control the overall framerate, only the rate at which the sprite cycles through its animations.


Maybe it is not intended, but in your program timePerFrame does in fact contol the overal framerate. And that is the problem...
shadowmouse, I'd really appreciate it if you'd show me the program with your ideas. Kevin C has helped me to realise that my solution is no solution at all since the overall frame rate is tied to how many animation cycles there are per second. I don't know how to change this or how to incorporate your suggestions.
http://gafferongames.com/game-physics/fix-your-timestep/

A huge help for learning about timesteps and why they are needed and the different variants.

Also it should be elapsed time * animationSpeed, not timePerFrame as you want animationSpeed to be bigger if it is going faster, rather than smaller as with timePerFrame.


Just wanted to comment you really do want to have a fixed timestep for both your physics and your animation code, having a variable timestep will induce some quite nasty bugs in your game. So you definitely do not want your animation code to be running faster the if the computers framerate is higher.

The only time you want your animation to run faster or to do a double update so to speak is during lag spikes where an animation update loop is delayed longer then the time that is in the animation's timePerFrame.


Anyways wish you the best of luck with this problem Bogeyman, just keep on doing a bit more research into the main game loop and timesteps in general and you should be able to fix the problem you are have now.
Last edited on
I really can't go around making full code at the moment, I have my own massive project to finish but I'd like to pick you up on something there Z e r e o is that because the counter is a float, it will increase by exactly the same amount in per amount of time. That is the point of the formula. If the computer was running at 60 fps, each frame the counter would be incremented by 1/60 * speed (which is constant) and the animation would not increment until the full second had passed because of floor. If it was running at 1 fps then the counter would be incremented by 1*speed every frame. Both of these are changing the animation be speed frames each second. That is the point of truly frame rate independent game logic.
Thanks for the encouragement Zereo. I'll have a look at the article and see if it helps.

No worries shadowmouse, thanks for trying to explain what to do. I hope your project goes well.
Topic archived. No new replies allowed.
Pages: 12