У меня есть следующий код для расчета перевода, необходимого для перемещения игрового объекта в Unity, который вызывается LateUpdate
. Из того, что я понимаю, мое использование Time.deltaTime
должно сделать окончательную частоту кадров перевода независимой (пожалуйста, обратите внимание, CollisionDetection.Move()
что я просто выполняю raycast).
public IMovementModel Move(IMovementModel model) {
this.model = model;
targetSpeed = (model.HorizontalInput + model.VerticalInput) * model.Speed;
model.CurrentSpeed = accelerateSpeed(model.CurrentSpeed, targetSpeed,
model.Accel);
if (model.IsJumping) {
model.AmountToMove = new Vector3(model.AmountToMove.x,
model.AmountToMove.y);
} else if (CollisionDetection.OnGround) {
model.AmountToMove = new Vector3(model.AmountToMove.x, 0);
}
model.FlipAnim = flipAnimation(targetSpeed);
// If we're ignoring gravity, then just use the vertical input.
// if it's 0, then we'll just float.
gravity = model.IgnoreGravity ? model.VerticalInput : 40f;
model.AmountToMove = new Vector3(model.CurrentSpeed, model.AmountToMove.y - gravity * Time.deltaTime);
model.FinalTransform =
CollisionDetection.Move(model.AmountToMove * Time.deltaTime,
model.BoxCollider.gameObject, model.IgnorePlayerLayer);
// Prevent the entity from moving too fast on the y-axis.
model.FinalTransform = new Vector3(model.FinalTransform.x,
Mathf.Clamp(model.FinalTransform.y, -1.0f, 1.0f),
model.FinalTransform.z);
return model;
}
private float accelerateSpeed(float currSpeed, float target, float accel) {
if (currSpeed == target) {
return currSpeed;
}
// Must currSpeed be increased or decreased to get closer to target
float dir = Mathf.Sign(target - currSpeed);
currSpeed += accel * Time.deltaTime * dir;
// If currSpeed has now passed Target then return Target, otherwise return currSpeed
return (dir == Mathf.Sign(target - currSpeed)) ? currSpeed : target;
}
private void OnMovementCalculated(IMovementModel model) {
transform.Translate(model.FinalTransform);
}
Если я фиксирую частоту кадров игры до 60FPS, мои объекты перемещаются, как и ожидалось. Однако, если я разблокирую его ( Application.targetFrameRate = -1;
), некоторые объекты будут двигаться гораздо медленнее, чем я ожидал бы при достижении ~ 200FPS на мониторе 144 Гц. Кажется, это происходит только в автономной сборке, а не в редакторе Unity.
GIF движения объекта в редакторе, разблокированный FPS
http://gfycat.com/SmugAnnualFugu
GIF движения объекта в автономной сборке, разблокированный FPS
источник
Ответы:
Моделирование на основе кадров будет вызывать ошибки, когда обновления не в состоянии компенсировать нелинейные скорости изменения.
Например, рассмотрим объект, начинающийся с нулевых значений положения и скорости, который испытывает постоянное ускорение, равное единице.
Если мы применим эту логику обновления:
Мы можем ожидать эти результаты при разных частотах кадров:
Ошибка вызвана обработкой конечной скорости, как если бы она применялась для всего кадра. Это похоже на сумму Правого Римана, и количество ошибок зависит от частоты кадров (показано на другой функции):
Как MichaelS указывает , эта ошибка будет уменьшена вдвое при уменьшении длительности кадра и может стать несущественной при высокой частоте кадров. С другой стороны, любые игры, которые испытывают скачки производительности или длительные кадры, могут обнаружить, что это приводит к непредсказуемому поведению.
К счастью, кинематика позволяет нам точно рассчитать смещение, вызванное линейным ускорением:
Так что, если мы применим эту логику обновления:
У нас будут следующие результаты:
источник
if(velocity==vmax||velocity==-vmax){acceleration=0}
. Затем ошибка существенно снижается, хотя она и не идеальна, поскольку мы точно не выясним, какая часть ускорения кадра закончилась.Это зависит от того, откуда вы звоните. Если вы вызываете его из Update, ваше движение действительно будет независимым от частоты кадров, если вы масштабируете с помощью Time.deltaTime, но если вы вызываете его из FixedUpdate, вам нужно масштабировать с Time.fixedDeltaTime. Я полагаю, что вы вызываете свой шаг из FixedUpdate, но масштабируете его с помощью Time.deltaTime, что приведет к снижению видимой скорости, когда фиксированный шаг Unity медленнее, чем основной цикл, что и происходит в вашей автономной сборке. Когда фиксированный шаг медленный, fixedDeltaTime велик.
источник
Time.deltaTime
что все равно будет использовать правильное значение независимо от того, где оно вызывается (если используется в FixedUpdate, он будет использовать fixedDeltaTime).