Mam następujący kod do obliczenia tłumaczenia wymaganego do przeniesienia obiektu gry w Unity, który jest wywoływany LateUpdate
. Z tego, co rozumiem, moje użycie Time.deltaTime
powinno uniezależnić ostateczną liczbę klatek na sekundę tłumaczenia (pamiętaj, CollisionDetection.Move()
że wykonuję raycasty).
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);
}
Jeśli zablokuję klatkę na sekundę w grze do 60 FPS, moje obiekty poruszają się zgodnie z oczekiwaniami. Jednak jeśli go odblokuję ( Application.targetFrameRate = -1;
), niektóre obiekty będą poruszać się znacznie wolniej niż oczekiwałbym po osiągnięciu ~ 200 klatek na sekundę na monitorze 144 Hz. Wydaje się, że dzieje się tak tylko w samodzielnej wersji, a nie w edytorze Unity.
GIF ruchu obiektu w edytorze, odblokowane FPS
http://gfycat.com/SmugAnnualFugu
GIF ruchu obiektu w ramach samodzielnej wersji, odblokowany FPS
źródło
Odpowiedzi:
W symulacjach opartych na ramkach wystąpią błędy, gdy aktualizacje nie zrekompensują nieliniowych szybkości zmian.
Na przykład rozważmy obiekt zaczynający się od wartości położenia i prędkości zerowej, doświadczający stałego przyspieszenia jednego.
Jeśli zastosujemy tę logikę aktualizacji:
Możemy spodziewać się tych wyników przy różnych częstotliwościach klatek:
Błąd jest spowodowany traktowaniem prędkości końcowej tak, jakby dotyczyła całej klatki. Jest to podobne do prawej sumy Riemanna, a ilość błędów zależy od liczby klatek na sekundę (zilustrowane inną funkcją):
Jak zauważa MichaelS , ten błąd zostanie zmniejszony o połowę, gdy czas trwania ramki zmniejszy się o połowę, i może stać się nieistotny przy wysokich częstotliwościach klatek. Z drugiej strony gry, w których występują gwałtowne wzrosty wydajności lub długotrwałe ramki, mogą powodować nieprzewidywalne zachowanie.
Na szczęście kinematyka pozwala nam dokładnie obliczyć przesunięcie spowodowane przyspieszeniem liniowym:
Jeśli zastosujemy tę logikę aktualizacji:
Będziemy mieć następujące wyniki:
źródło
if(velocity==vmax||velocity==-vmax){acceleration=0}
. Następnie błąd znacznie spada, choć nie jest idealny, ponieważ nie wiemy dokładnie, która część przyspieszenia klatki zakończyła się.To zależy od tego, skąd dzwonisz. Jeśli wywołujesz go z aktualizacji, twój ruch rzeczywiście będzie niezależny od klatek na sekundę, jeśli skalujesz za pomocą Time.deltaTime, ale jeśli dzwonisz z FixedUpdate, musisz skalować za pomocą Time.fixedDeltaTime. Wydaje mi się, że nazywasz swój krok z FixedUpdate, ale skalujesz za pomocą Time.deltaTime, co spowodowałoby zmniejszenie prędkości pozornej, gdy stały krok Unity jest wolniejszy niż główna pętla, co dzieje się w twojej samodzielnej wersji. Gdy ustalony krok jest wolny, parametr fixedDeltaTime jest duży.
źródło
Time.deltaTime
że nadal użyje poprawnej wartości, niezależnie od tego, gdzie się nazywa (jeśli zostanie użyta w FixedUpdate, użyje fixedDeltaTime).