ru:toolworks:docs:apparatus:steady-tick

Steady Ticking

Почти все видео-игры (и, следовательно, игровые движку) имеют в общем случае технику итерирования игрового процесса. Игра по сути является циклом. Это цикл так и называется - игровой цикл. В каждой итерации игрового цикла игра должна приготовить и актуализировать своё состояние, в частности, проработать рендеринг, оценить производительность и нарисовать графику. В рамках Unreal Engine эти итерации называются тиками (ticks). Это что-то наподобие сердца игры.

Как и сердцебиение человека, временные интервалы тиков различаются; то есть изменяется время, необходимое для того, чтобы обработать и отрисовать новый кадр. Вы можете неожиданно получить кадр, вычислительная или графическая нагрузка на котором будет достаточно высокая, и время тика увеличится.


Чтобы игровая логика актуализировала состояние игры, этот меняющийся временной интервал (delta time) подаётся ей на вход как вещественное значение, чтобы построить новый «шаг» на основе предыдущего. В Unreal Engine это делается при помощи ноды-события Tick, а конкретно, при помощи её аргумента Delta Seconds:

Техника рабочая, например, когда вы играете в игру с заранее вычисленной анимацией, где результат всегда определён во времени. К несчастью, так бывает не всегда, так как игры в большинстве своём интерактивны и нагрузка меняется от кадра к кадру.

Если применить переменное время кадра к такому процессу, как просчитывание коллизий, то их устойчивость будет значительно падать, если время тика увеличится. Рассмотрим следующий пример.

Пусть у нас есть шар с симуляцией физики. Шар сдвигается со своей позиции в зависимости от своей скорости (которая определяется на основе гравитации и других действующих сил). Когда время тика стабильно и достаточно мало, всё работает, как ожидалось. Но как только один из кадров «даёт сбой», система сразу же становится крайне нестабильной и мяч с лёгкостью может проскочить сквозь пол, что приводит к совершенно некорректному поведению.


Наша цель - стабилизировать время кадра и нормализовать вычисления.


В физике Unreal Engine есть способ стабилизировать время кадра. Это достигается ограничением максимального времени тика:

Если текущий кадр занимает большее время, чем заданное значение, физика движка получит это значение как свой delta time, для вычисления текущего шага. Это, в свою очередь, вызовет в замедление игрового времени. Что-то наподобие slow-mo эффекта, а симуляция не потеряет своей стабильности, то есть, стабильность будет не ниже, чем предоставленное допустимое значение.

Другой способ достичь стабильности физики - использовать такую технику Unreal Engine, как sub-stepping. Для большей информации по этой теме можете посмотреть официальную документацию по ссылке.

Apparatus предоставляет схожую sub-stepping-гу концепцию, которую мы называем «Steady Ticking». Каждый Mechanism реализовывает специальное событие Steady Tick:

Устойчивые тики имеют фиксированный временной интервал и выполняются параллельно основным тикам (event-tick), у которых этот интервал может меняться. Слово «параллельно» совершенно не означает мульти-поточность, напротив, подчёркивает логику вызовов.


На картинке выше вы могли заметить, что может произойти несколько устойчивых тиков за один переменный тик. Также, как и несколько переменных тиков могло произойти за один устойчивый. Такие различия могут привести к нежелательным резким перемещениям объектов (или выполнения другой логики). Чтобы решить этот вопрос нужна интерполяция.

В течение переменного «родного» тика мы интерполируемся между предыдущим устойчивым тиком и следующим. Сам пользователь может выполнять необходимую интерполяцию, но Apparatus уже предоставляет требуемый функциональный базис, чтобы это сделать.

Для начала рассмотрим ноду Steady Frame Ratio (соотношение устойчивых кадров):

Эта нода возвращает отношение текущего переменного тика к активному устойчивому. Для лучшего понимания, просто посмотрите эту схему:


Это отношение, являясь числом из отрезка [0.0, 1.0], может быть использовано для интерполяции анимации на основе её предыдущей и следующей состояний. Чтобы этого достичь, ваши механики, выполняемые устойчивыми тиками, должны подготовить оба эти состояния в течении своего времени выполнения. Обычный же (переменный) тик может использовать ноду Lerp, чтобы обеспечить непосредственно гладкость и неразрывность.

Иногда бывает утомительно (или довольно трудно) управлять обеими предыдущим и следующим состояниями в некоторых вспомогательных переменных. Вы, возможно, захотите использовать текущее состояние объекта, вместо предыдущего, - например, можно использовать transform-вектор для вычисления следующего шага. Поэтому мы предлагаем вам метод, который удобно использовать и при таком подходе, - нода Steady Future Factor.

Эта нода несколько похожа на Steady Frame Ratio, по крайней мере тем, что возвращает единственное число с плавающей точкой из отрезка [0.0, 1.0]. Но отличается она, на самом деле, причиной, по которой выходные значения лежат именно в таком промежутке. Проиллюстрируем это на схеме:


Как вы можете видеть, последнее изменение (было оно в устойчивом или обычном тике) отслеживается и запоминается в качестве стартовой точки для отношения. Вторая часть такая же, как и для узла Steady Frame Factor, и фактически представляет собой временную метку следующего будущего устойчивого кадра/*не понял предложения, прогнал через Google-translate: The second part is the same as for the Steady Frame Factor node and actually represents a timestamp of the next future steady frame.*/. Используя ноду Steady Future Factor, вы можете использовать текущее актуальное состояние как базу для Lerp„а со следующим состоянием. Просто будьте внимательны к своим вычислениям и убедитесь в том, что в реально поддерживаете трек того, что вы хотите интерполировать.

Обе эти ноды могут быть использованы прямо в tick-событиях ваших механик. Однако, мы имплементировали специальное событие для механик, так называемое Presentation Tick-событие, которая естественным образом связывает вышеперечисленные pure-функции:

Это событие наступает после нормальных переменных тиков (а значит, и после тиков устойчивых) и подразумевает использование для вычисления визуального отклика механик. Интерполяция механик, очевидно, тоже может быть вычислена. Посмотрите на этот всеобъемлющий пример механики интерполяции 3d-положения объекта (выдержка из нашего примера-платформера):

Самая важная Apply Offset функция выглядит так:

Завершая тему статьи, следовало бы сказать о том, что описанные техники вовсе не являются обязательными к применению. Вы можете выполнять свои механики так, как захотите, и настраивать движок так, чтобы он обрабатывал только определённые FPS. Мы же, тем не менее, предлагаем вам путь для достижения стабильной частоты кадров геймплея и также рекомендуем использовать его для поддержания стабильности.

  • ru/toolworks/docs/apparatus/steady-tick.txt
  • Последнее изменение: 2021/04/21 04:29
  • jispar