Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SFML Game Development.pdf
Скачиваний:
194
Добавлен:
28.03.2016
Размер:
4.19 Mб
Скачать

Chapter 1

Frame-independent movement

If you run everything we have done so far, you will be able to move the circle, but it won't move uniformly. It will probably be very fast, because currently we have done the movement in a very naive way. Right now your computer will be running the update() function as fast as it can, which means it will probably call it a couple of hundreds of times each second, if not more. If we move the shape by one pixel for every frame, this can count up to several 100 pixels every second, making our little player fly all over the screen. You cannot just change the movement value to something lower, as it will only fix the problem for your computer. If you move to a slower or faster computer, the speed will change again.

So how do we solve this? Well, let's look at the problem we are facing. We are having a problem because our movement is frame-dependent. We want to provide the speed in a way that changes depending on the time a frame takes. There is a simple formula you should remember from your old school days. It's the formula that goes: distance = speed * time. Now why is this relevant for us? Because with this formula we can calculate a relevant speed for every frame, so that the circle always travels exactly the distance we want it to travel over one second, no matter what computer we are sitting on. So let's modify the function to what we actually need to make this work.

void Game::update(sf::Time deltaTime)

{

sf::Vector2f movement(0.f, 0.f); if (mIsMovingUp)

movement.y -= PlayerSpeed; if (mIsMovingDown)

movement.y += PlayerSpeed; if (mIsMovingLeft)

movement.x -= PlayerSpeed; if (mIsMovingRight)

movement.x += PlayerSpeed;

mPlayer.move(movement * deltaTime.asSeconds());

}

The major difference we have made here is that we now receive a time value every time we call the update. We calculate the distance we want to travel every frame, depending on how much time has elapsed. We call the time that has elapsed since the last frame delta time (or time step), and often abbreviate it as dt in the code. But how do we get this time? We are lucky because SFML provides the utilities for it.

[ 21 ]

www.it-ebooks.info

Making a Game Tick

In SFML, there is a class that measures the time from when it was started. What we have to do is to measure the time each frame takes. We are talking about the class sf::Clock. It has a function called restart(), which lets the clock return the elapsed time since its start, and restarts the clock from zero, making it ideal for our current situation. SFML uses the class sf::Time for all time formats; it is a convenient data type that can be converted from and to seconds, milliseconds, and microseconds. Here's the modified Game::run() member function:

void Game::run()

{

sf::Clock clock;

while (mWindow.isOpen())

{

sf::Time deltaTime = clock.restart(); processEvents();

update(deltaTime);

render();

}

}

There is no big difference; we create a clock, and in every frame we query it for its current elapsed time, restart the clock, and then pass this time to the update function.

Fixed time steps

The solution we have come up with so far is sufficient for many cases. But it is not a perfect solution, because you can have problems in certain scenarios where delta times vary strongly. The code can be quite hard to debug, because it is impossible to get 100 percent reproducible results, since every frame is unique, and you can't guarantee that the delta time remains the same.

Consider that a frame may sometimes take three times the average delta time. This can lead to severe mistakes in the game logic, for example, when a player moves three times the distance and passes through a wall he would normally collide with.

This is why physics engines expect the delta time to be fixed.

[ 22 ]

www.it-ebooks.info

Chapter 1

The following is a figure describing the problem we are referring to:

Normal

 

 

Lag

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

What we will do now is use a technique called fixed time steps. We write code that guarantees that in any circumstances, we always give the same delta time to the update function, no matter what happens. If you find that sounding difficult, there is no big difference from what we already have. We just have to do some book-keeping in our code for how much time has passed since we last called the update() function.

void Game::run()

{

sf::Clock clock;

sf::Time timeSinceLastUpdate = sf::Time::Zero; while (mWindow.isOpen())

{

processEvents();

timeSinceLastUpdate += clock.restart(); while (timeSinceLastUpdate > TimePerFrame)

{

timeSinceLastUpdate -= TimePerFrame; processEvents(); update(TimePerFrame);

}

render();

}

}

The actual effect of this change is that we accumulate how much time has elapsed in a variable timeSinceLastUpdate. When we are over the required amount for one frame, we subtract the desired length of this frame (namely TimePerFrame), and update the game. We do this until we are below the required amount again. This solves the problem with variable delta times, as we are guaranteed that the same amount of frames is always run. In the application you can download, the logic frame rate will be set to 60 frames per second by having the TimePerFrame constant

equal to sf::seconds(1.f / 60.f).

[ 23 ]

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]