#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
как сумму, которую нужно применить в течение этого фиксированного временного шага (т. Е. Значение не будет скорректировано по времени).