官术网_书友最值得收藏!

Using the SFML clock

The sf::Clock class is very simple and lightweight, so it has only two methods: getElapsedTime() and restart(). Its sole purpose is to measure elapsed time since the last instance of the clock being restarted, or since its creation, in the most precise manner the operating system can provide. When retrieving the elapsed time using the getElapsedTime method, it returns a type sf::Time. The main reasoning behind that is an additional layer of abstraction to provide flexibility and avoid imposing any fixed data types. The sf::Time class is also lightweight and provides three useful methods for conversion of elapsed time to seconds which returns a floating point value, milliseconds, which returns a 32-bit integer value and microseconds, which returns a 64-bit integer value, as represented here:

sf::Clock clock;
...
sf::Time time = clock.getElapsedTime();

float seconds = time.asSeconds();
sf::Int32 milliseconds = time.asMilliseconds();
sf::Int64 microseconds = time.asMicroseconds();
...
time = clock.restart();

As you can see, the restart method also returns an sf::Time value. This is provided in order to avoid calling getElapsedTime right before calling the restart method and having some time pass between those two calls that would otherwise be unaccounted for. How is this useful for us? Well, the problem we were dealing with was the same code running differently on other platforms because we couldn't account for their speed. We moved our sprite across the screen using this line of code:

m_mushroom.setPosition(
    m_mushroom.getPosition().x + m_increment.x, 
    m_mushroom.getPosition().y + m_increment.y);

The m_increment vector here is used with an assumption that the time between iterations is constant, but that's obviously not true. Recall the magic triangle for the speed, time, and distance formula:

Finding the distance a sprite should travel in between updates can be done by first defining a set speed at which it moves. The time value here is simply how long it takes for an entire cycle of the program to finish. In order to accurately measure that, we're going to be adjusting the Game class to utilize the sf::Clock:

class Game{
public:
...
    sf::Time GetElapsed();
    void RestartClock();
private:
...
    sf::Clock m_clock;
    sf::Time m_elapsed;
...
};

The two new public methods we've added can be implemented like so:

sf::Time Game::GetElapsed(){ return m_elapsed; }
void Game::RestartClock(){ m_elapsed = m_clock.restart(); }

Once that is done, it's important to actually utilize this functionality and restart the game clock after each iteration. That can be achieved in the main game loop by simply calling the RestartClock method after all the work is done:

while(!game.GetWindow()->IsDone()){
    // Game loop.
    game.HandleInput();
    game.Update();
    game.Render();
    game.RestartClock(); // Restarting our clock.
}

The last line in the loop will make sure that the m_elapsed member of the game class will always have a value of the time passed during the previous iteration, so let's use that time and determine how far our sprite should have moved:

float fElapsed = m_elapsed.asSeconds();

m_mushroom.setPosition(
    m_mushroom.getPosition().x + (m_increment.x * fElapsed), 
    m_mushroom.getPosition().y + (m_increment.y * fElapsed));

We're now using m_increment as a variable of speed, not distance. By looking at our previous code in the constructor, we've set both x and y values of the m_increment vector to a value of 4. Since we're expressing our elapsed time as seconds, this is essentially like saying that the sprite needs to move 4 pixels a second. That's really slow, so let's change it to something a little bit more stimulating:

Game::Game(){
...
    m_increment = sf::Vector2i(400,400); // 400px a second.
}

Upon compiling and running the project, you should see our sprite happily bouncing across the screen. It will now be moved the same distance on every single machine it's executed on, no matter how choppy the frame-rate is. For extra points, try it out yourself by artificially slowing down the game loop with the sf::sleep function that SFML provides, like so:

while(!game.GetWindow()->IsDone()){
    // Game loop.
    game.HandleInput();
    game.Update();
    game.Render();
    sf::sleep(sf::seconds(0.2)); // Sleep for 0.2 seconds.
    game.RestartClock();
}

Feel free to adjust the argument passed to the sleep function. You will notice that it moves the sprite across exactly the same distance, no matter how long each iteration takes to finish.

主站蜘蛛池模板: 昭苏县| 宁阳县| 黑山县| 璧山县| 东辽县| 敦化市| 永嘉县| 长治市| 玉门市| 睢宁县| 申扎县| 永年县| 松滋市| 兴文县| 杭州市| 眉山市| 都匀市| 屏东县| 五河县| 应城市| 镇康县| 边坝县| 关岭| 肇东市| 潢川县| 珠海市| 白银市| 定兴县| 三原县| 万州区| 泸水县| 抚宁县| 大竹县| 万盛区| 武夷山市| 孟州市| 慈利县| 东宁县| 八宿县| 芜湖市| 万全县|