Eu tenho o código a seguir para calcular a tradução necessária para mover um objeto de jogo no Unity, que é chamado LateUpdate
. Pelo que entendi, meu uso de Time.deltaTime
deve tornar a taxa de quadros de tradução final independente (observe CollisionDetection.Move()
apenas o desempenho de raycasts).
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);
}
Se eu bloquear a taxa de quadros do jogo para 60FPS, meus objetos se moverão conforme o esperado. No entanto, se eu o desbloquear ( Application.targetFrameRate = -1;
), alguns objetos se moverão a uma taxa muito mais lenta do que eu esperaria ao atingir ~ 200FPS em um monitor de 144 hz. Isso parece acontecer apenas em uma construção independente, e não no editor do Unity.
GIF do movimento do objeto dentro do editor, FPS desbloqueado
http://gfycat.com/SmugAnnualFugu
GIF de movimento de objetos na construção autônoma, FPS desbloqueado
fonte
Respostas:
As simulações baseadas em quadros sofrerão erros quando as atualizações falharem em compensar taxas de mudança não lineares.
Por exemplo, considere um objeto começando com valores de posição e velocidade de zero experimentando uma aceleração constante de um.
Se aplicarmos essa lógica de atualização:
Podemos esperar estes resultados sob diferentes taxas de quadros:
O erro é causado pelo tratamento da velocidade final como se ela fosse aplicada a todo o quadro. Isso é semelhante a uma soma direita de Riemann e a quantidade de erro varia com a taxa de quadros (ilustrada em uma função diferente):
Como o MichaelS aponta, esse erro será reduzido pela metade quando a duração do quadro for reduzida pela metade e poderá se tornar inconseqüente em altas taxas de quadros. Por outro lado, qualquer jogo com picos de desempenho ou quadros de execução prolongada pode achar que isso produz um comportamento imprevisível.
Felizmente, a cinemática nos permite calcular com precisão o deslocamento causado pela aceleração linear:
Portanto, se aplicarmos essa lógica de atualização:
Teremos os seguintes resultados:
fonte
if(velocity==vmax||velocity==-vmax){acceleration=0}
. Então, o erro diminui substancialmente, embora não seja perfeito, pois não descobrimos exatamente qual parte da aceleração do quadro terminou.Depende de onde você está chamando o seu passo. Se você está chamando de Atualização, seu movimento será de fato independente da taxa de quadros se você escalar com Time.deltaTime, mas se você estiver chamando de FixedUpdate, precisará escalar com Time.fixedDeltaTime. Eu acho que você está chamando seu passo de FixedUpdate, mas escalando com Time.deltaTime, o que resultaria em menor velocidade aparente quando o passo fixo do Unity for mais lento que o loop principal, que é o que está acontecendo em sua compilação independente. Quando a etapa fixa é lenta, fixedDeltaTime é grande.
fonte
Time.deltaTime
que ainda usará o valor correto, independentemente de onde é chamado (se usado em FixedUpdate, ele usará FixedDeltaTime).