Когда вы умножаете на Time.deltaTime в Unity?

#unity3d #game-physics

#unity3d #игра-физика

Вопрос:

Кажется, есть много запутанных / противоречивых советов о том, когда вы должны или не должны умножать количество на Time.deltaTime . Итак, мне было интересно, поскольку это относится к физической системе Unity, когда вы должны умножать на Time.deltaTime ?

Я понимаю, что Time.deltaTime используется для того, чтобы сделать определенные действия независимыми от частоты кадров, но есть некоторые ситуации, когда неясно, зависит ли действие от частоты кадров. Например:

  • Выполняем rigidbody.velocity = acceleration*Time.deltaTime; в FixedUpdate. Поскольку FixedUpdate выполняется с постоянным временным интервалом, делает ли это умножение на Time.deltaTime ненужным?
  • Выполняем rigidbody.AddForce(force*Time.deltaTime, forceMode); . Как различные ForceMode s влияют на это?

Я видел решения для отдельных ситуаций, но было бы неплохо, если бы была простая интуиция, которая могла бы решить все эти ситуации.

Ответ №1:

Интуиция

Умножение на Time.deltaTime используется для того, чтобы мгновенная операция выполнялась непрерывно (или «плавно»).

Мгновенная операция приведет к тому, что определенная величина «перейдет» к другому значению. Следующие операции выполняются мгновенно:

  • Rigidbody.[position|velocity|rotation|angularVelocity] = x;
  • Rigidbody2D.[position|velocity|rotation|angularVelocity] = x;
  • Rigidbody.Add[Force|Torque](x, ForceMode.[Impulse|VelocityChange]);
  • Rigidbody2D.Add[Force|Torque](x, ForceMode2D.Impulse);

Все вышеуказанные операции приводят к мгновенному скачку величины на x , независимо от длины кадра.

В отличие от этого, следующие операции являются непрерывными:

  • Rigidbody.Add[Force|Torque](x, ForceMode.[Force|Acceleration]);
  • Rigidbody2D.Add[Force|Torque](x, ForceMode.Force);
  • Rigidbody.velocity = x; (непрерывный с точки зрения Rigidbody.position . т. Е. Установка скорости не заставит Rigidbody.position внезапно перейти к новому значению)

Все вышеуказанные операции применяются в течение кадра (по крайней мере, концептуально это происходит; то, что физическая система делает внутренне, выходит за рамки этого ответа). Чтобы проиллюстрировать это, Rigidbody.AddForce(1, ForceMode.Force) вызовет приложение силы в 1 ньютон к объекту для всего кадра; не будет резких скачков положения или скорости.

Итак, когда вы умножаете на Time.deltaTime ?

Если вы используете непрерывную операцию, вы почти наверняка не должны умножать на Time.deltaTime . Но если вы используете мгновенную операцию, ответ зависит от того, используется ли операция непрерывно.

Пример 1: Взрыв

 void Explode() {
    rigidbody.velocity  = explosionAcceleration;
}
  

Здесь вы не должны умножать на, Time.deltaTime поскольку Explode() вызывается только один раз. Это не предназначено для применения к серии кадров.

Пример 2: Перемещение

 void Update() {
    rigidbody.position  = velocity * Time.deltaTime;
}
  

Здесь вам нужно умножить на Time.deltaTime , потому что объект должен непрерывно перемещаться с течением времени, и вы также используете мгновенную операцию. Обратите внимание, что это можно заменить непрерывной операцией и устранить необходимость умножения на Time.deltaTime :

 void Update() {
    rigidbody.velocity = velocity;
}
  

Хотя это не совсем эквивалентно оригиналу, поскольку оно игнорирует предыдущее значение rigidbody.velocity .

Пример 3: Ускорение

 void Update() {
    rigidbody.AddForce(acceleration*Time.deltaTime, ForceMode.VelocityChange);
}
  

Здесь вы должны умножать на Time.deltaTime , потому что вы хотите, чтобы скорость увеличивалась с постоянной скоростью кадр за кадром. Обратите внимание, что это точно эквивалентно:

 void Update() {
    rigidbody.AddForce(acceleration, ForceMode.Acceleration);
}
  

Этот код применяет ускорение в течение всего кадра. Этот код также (на мой взгляд) чище и проще для понимания.

Как насчет FixedUpdate ?

Пребывание в FixedUpdate не влияет ни на один из вышеприведенных советов. Да, теоретически вам может сойти с рук никогда не умножать на Time.deltaTime , поскольку время между кадрами всегда будет одинаковым. Но тогда все ваши единицы должны были бы зависеть от фиксированной частоты кадров. Так, например, если бы вы хотели перемещаться со скоростью 1 m/s с 60 кадрами в секунду, вам пришлось бы добавлять 1/60=.01666 к позиции каждый кадр. А затем представьте, что произойдет, если вы измените свой фиксированный временной шаг. Вам пришлось бы изменить кучу ваших констант, чтобы приспособиться.

Выполнение кода в FixedUpdate не делает внезапно частоту кадров кода независимой. Вы все равно должны принять те же меры предосторожности, что и в Update .

В качестве примечания, вам не нужно заменять Time.deltaTime на Time.fixedDeltaTime inside FixedUpdate , потому что Unity уже выполняет эту замену.

Заключение

Умножайте только на Time.deltaTime , если вы хотите, чтобы мгновенная операция выполнялась плавно в течение ряда кадров.

Во многих случаях вы можете избежать использования Time.deltaTime , используя непрерывные операции, которые часто более интуитивно понятны (см. Примеры 2 и 3). Но это, конечно, зависит от контекста.

Ответ №2:

Простой способ подумать об этом — определить:

Выполняются ли преобразования объектов напрямую?

Когда объект перемещается непосредственно через его transform.position и transform.rotation компоненты, манипуляция с преобразованием должна происходить в Update() , которая выполняется один раз за нарисованный кадр. Разработчик должен создавать свою игру с ожиданием, что обновление / частота кадров будет варьироваться между нулем и максимальной частотой кадров в игре. Способ добиться этого — разложить время между настоящим моментом и предыдущим кадром на движение (шаг) для каждого обновления / кадра.

Таким образом, манипуляции с transform.position и transform.rotation должны учитываться Time.deltaTime и должны происходить внутри Update() .

Выполняются ли физические манипуляции с объектами?

В этом случае силы и крутящие моменты должны быть применены путем вызова Rigidbody.AddForce() или Rigidbody.AddTorque() в FixedUpdate() . FixedUpdate() можно ожидать, что это произойдет с фиксированной / гарантированной скоростью, которая в Unity по умолчанию равна 50 раз в секунду (это можно настроить в настройках физики вашего игрового проекта).

Чтобы немного усложнить ситуацию, когда AddForce() и AddTorque() вызываются с помощью ForceMode.Force (по умолчанию) or ForceMode.Acceleration , значение, указанное для параметров force or torque , будет интерпретироваться как значения за целую секунду, поэтому эти функции будут корректировать значение (сила / крутящий момент или ускорение / угловой разгон) за один фиксированный временной шаг (т. Е. x 0.02 с с фиксированной частотой обновления Unity Physics по умолчанию).).

В случае, если вам интересно ForceMode.Impulse или ForceMode.VelocityChange , они будут интерпретировать force или torque как сумму, которую нужно применить в течение этого фиксированного временного шага (т. Е. Значение не будет скорректировано по времени).