Игры с независимой частотой кадров это игры, которые работают с одной скоростью, ничего не случится с частотой кадров. Например, игра может работать с частотой 30 FPS (Кадров в секунду) на медленном компьютере, и 60 FPS на быстром. Игры с независимой частотой кадров прогрессируют с одной скоростью на разных компьютерах (объекты движутся с той-же скоростью). С другой стороны, игры с зависимой частотой кадров прогрессирует в половину скорости на медленном компьютере, как эффект замедления времени. Создание независимой частоты кадров в играх важно, чтобы ваши игры были удовлетворительными и играбельными для каждого, независимо от того, какой компьютер есть у меня. Игры, в которые замедляются, когда частота кадров проваливается, может серьёзно сказаться на геймплее, заставляет игроков разочаровываться и уходить!
Данный туториал расскажет, как сделать вашу игру с независимой частотой кадров. Некоторые техники также включают размерность времени, который позволяет делать эффект замедления времени и простую паузу.
dt системное выражение
Ключ к независимости частоты кадров, это системное выражение dt. dt сокращение от delta-time. Delta означает смену качества, т.е. delta-time означает смену во времени. Это время в секундах, которое прошло с прошлого тика.
Например, на 100 fps dt будет 0.01 (одна сотая от секунды), и от 10 fps dt будет 0.1 (одна десятая секунда). В практике, dt изменяется с тик по тик, так что, вряд ли это будет одно и то же значение в течении продолжительного периода времени.
Но если вы будете добавлять dt к переменной каждое мгновение, это будет добавляться 1 каждую секунду, потому-что время между всеми тиками в одной секунде должно прибавлять 1! Здесь пример показывающий, как. (Добавление dt к локальным переменным объекта, это такой же удобный способ, чтобы иметь таймер у объекта.)
Как использовать
dt
Обычно, покадрово-зависимое движение достигается с использование эвента:
Каждый миг (раз в кадр), объект двигается на один пиксель вправо. т.е 30 FPS означает 30 пикселей в секунду, и 60 FPS означает 60 пикселей в секунду. Это различные скорости, зависимые от частоты кадров.
Запомните с этого примера, что dt всегда добавляет до 1 каждую секунду. Итак, если мы сменим событие на следующее:
...объект будет двигаться 60 кадров в секунду с любой частотой смены кадров. т.к dt добавляет до 1 каждую секунду, 60 [] dt* добавляет до 60 каждую секунду. Это значит, что на разных 30 FPS и 60 FPS наш объект будет двигаться со скоростью 60 пикселей в секунду - одна скорость, несмотря на частоту смены кадров.
Использование
dt везде
Каждый раз, двигая объект с постоянной скоростью, вам надо использовать dt в случае, если вам нужна покадровая независимость. Например, у спрайта Move forward действие берёт количество пикселей, для передвижения спрайта вперёд. если вы всегда двигаете объект вперёд, вы можете двигать его вперёд на 60 [] dt* пикселей это 60 пикселей в секунду с текущим углом.
Поведения, использующие
dt
Все поведения Construct 2 используют dt в их расчётах движения. Это значит, что ни одно поведение, которое двигает не требует любой обработки - Они уже обработаны!
Обратите внимание, что Physics это выражение. Обычно, это не использует dt, и следовательно, покадрово зависим. Это потому-что dt обычно имеют маленькие рандомные вариации. These variations can make the same setup in a physics game give different results even if exactly the same thing is done twice. This is often annoying for physics games, so by default it is framerate dependent. However, you can enable use of dt by using the Set Stepping Mode physics action on start of layout, and choose Framerate independent. Note in this mode it still clamps the maximum timestep for physics to 1/30 (about 33ms), because using a very large timestep can cause instability in physics simulations.
Timescaling
A really cool feature in Construct 2 is timescaling. This allows you to change the rate time passes at in the game, also known as the time scale. You can set the time scale with the system Set Time Scale action. A time scale of 1 means normal speed. 0.5 means half as fast, and 2.0 means twice as fast. If you set your game's time scale to 0.1, it's going ten times slower but still smoothly - a nice slow-motion effect!
Timescaling works by changing the value returned by dt. This means behaviors are affected, and any movement using dt. If you don't use dt in your movement calculations (like the first event above) the motion is not affected by the time scale! So to use time scaling, you simply have to use dt properly in all movement.
Pausing
You can set the time scale to 0. This stops all motion. It's an easy way to pause the game. Set it back to 1 and the game will resume.
You might notice you can still do things like shoot using the game controls. You can get around that by putting your main game events in a group, and activating/deactivating that group as you pause and unpause.
It's also a good way to test you have used dt correctly. If you have used it correctly, setting the time scale to 0 will stop everything in the game. If you have not used it correctly, some objects might continue to move, even though the game is supposed to be paused! In that case you can check how those objects are moved, and make sure you are using dt properly.
Other kinds of movement
It's important to realise that dt must be used for all kinds of motion. This includes rotation and acceleration.
Rotation
Similar to before, the following event rotates the piggy 1 degree every tick:
This is 30 degrees per second at 30 FPS, and 60 degrees per second at 60 FPS - again, different speeds for different framerates. Using dt in the same way solves the problem again. This way the piggy rotates at 60 degrees per second regardless of the framerate:
Acceleration
Acceleration is also fairly straightforward. Usually this only applies when you are making a custom movement via events.
If you have a speed variable, your object will be moving at Object.Speed [] dt* pixels per tick. Your object's speed variable therefore contains a speed in pixels per second.
Suppose you want to accelerate the object by 100 pixels per second over one second. You simply need to add 100 [] dt to the object's speed variable every tick, and it will accelerate in a framerate independent way. In other words, you use dt* both to adjust the object's position, and to adjust the object's speed.
Lerp
If lerping from two fixed positions, simply ensure the factor (x in lerp(a, b, x)) increases using dt like any other moving value. A more complicated case is when lerping from the last result of lerp, e.g.
Set X to lerp(Self.X, TargetX, factor)
In this case the correct form is to use:
lerp(a, b, 1 - f ^ dt)
...where f is between 0 and 1, e.g. 0.5. Commonly lerp(a, b, f [] dt)* is used instead, but this is not perfectly accurate and has some other pitfalls. The maths involved is explained in more detail in the blog post Using lerp with delta-time.
Common mistakes
Never use dt in the Every X seconds condition! An event like Every 1 second will run every 1 second regardless of the framerate, thus is already framerate independent. The condition measures time, not frames. If you make an event like Every 60 [] dt seconds, you have inadvertently made the condition framerate dependent - the opposite of what you want to achieve! Such a condition will run every 6 seconds at 10 FPS (when dt = 0.1), or every 0.6 seconds at 100 FPS (when dt = 0.01); if you just use Every 6 seconds*, it already runs every 6 seconds regardless of the framerate.
Advanced considerations
Minimum framerate
At very low framerates, dt can become very large. For example, at 5 FPS, dt is 0.2. An object moving at 500 pixels per second is therefore moving 100 pixels per tick. This can cause it to "teleport" through walls or miss collisions with other objects.
Games are usually unplayable at such low framerates, but it is even worse if they become unstable like that. To help the game stay reliable even at very low framerates, Construct 2 does not let dt get larger than 0.1. In other words, below 10 FPS, dt stays at 0.1. This does also mean below 10 FPS the game starts going in to a slow-motion effect (described earlier as one of the issues of framerate dependent games), however this is usually a better result than the "teleporting objects" problem.
Random variation
As mentioned with physics, dt usually has small and random variations, usually due to imperfect timers in the computer. Like with physics, this can also cause slight random variations in your game. However, usually the effect is negligable and much less noticable than with physics. It's recommended that you always use dt in your games unless exact precision is required (which is rare).
Object time scales
You can set a time scale for an individual object, via the system Set object time scale action. This allows you to have, for example, a game running in slow-motion with a time scale of 0.3, but still have the player acting at full speed with a time scale of 1. This is achieved by setting the time scale to 0.3, then using Set object time scale to set the player's time scale. The system expression dt is only affected by the game time scale. Objects have their own dt expression (e.g. Player.dt) which you must use instead of the system dt for all motion relating to that object. This way, there are now two values of dt: one for the game, and one for the player. Since they return different values, different parts of the game can progress at different rates.
In this example, to return the player back to game time, use the system Restore object time scale action.
Conclusion
It's important to design your game from the start using dt. This improves the gameplay, ensuring the pace never slows down in particularly intense parts of the game. As an extra bonus, you can also use the time scaling feature, easily pause the game, and even control object's individual time scales.
Don't forget behaviors already use dt (except with Physics where you must turn it on yourself). If you use behaviors to control all motion in your game, you don't have to worry about dt at all! However, most games have some event-controlled motion, and it's important to remember how to make it framerate independent.