Time.deltaTime을 사용하더라도 움직임은 프레임 속도에 따라 달라집니다


13

Unity에서 게임 오브젝트를 이동하는 데 필요한 번역을 계산하는 다음 코드가 LateUpdate있습니다. 내가 이해 한 바에 Time.deltaTime따르면 최종 변환 프레임 속도를 독립적 으로 사용해야합니다 (레이 CollisionDetection.Move()캐스트를 수행하는 것입니다).

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;) 일부 객체가 훨씬 느린 속도로 이동하므로 144hz 모니터에서 ~ 200FPS를 달성 할 때 예상됩니다. 이것은 Unity 에디터가 아닌 독립형 빌드에서만 발생하는 것으로 보입니다.

에디터 내에서 오브젝트 이동 GIF, 잠금 해제 FPS

http://gfycat.com/SmugAnnualFugu

독립형 빌드 내에서 객체 이동 GIF, 잠금 해제 FPS

http://gfycat.com/OldAmpleJuliabutterfly


2
이 내용을 읽어야합니다. 시간 버킷 팅은 원하는 것이며 고정 시간 간격입니다! gafferongames.com/game-physics/fix-your-timestep
Alan Wolfe

답변:


30

업데이트가 비선형 변화율을 보정하지 못하면 프레임 기반 시뮬레이션에서 오류가 발생합니다.

예를 들어, 1의 일정한 가속을 겪는 위치와 속도 값이 0으로 시작하는 물체를 생각해보십시오.

이 업데이트 논리를 적용하면 :

velocity += acceleration * elapsedTime
position += velocity * elapsedTime

다른 프레임 속도에서 이러한 결과를 기대할 수 있습니다. 여기에 이미지 설명을 입력하십시오

전체 프레임에 적용되는 것처럼 최종 속도를 처리하여 오류가 발생합니다. 이것은 Right Riemann Sum 과 유사하며 오류의 양은 프레임 속도에 따라 다릅니다 (다른 기능에 설명되어 있음).

마이클스 지적한 이 오류는 프레임 지속 시간이 절반으로 줄어들면 절반으로 줄어들고 높은 프레임 속도에서는 중요하지 않을 수 있습니다. 반면에 성능이 급상승하거나 장시간 실행되는 게임은 예상치 못한 동작을 유발할 수 있습니다.


운 좋게 운동학을 사용하면 선형 가속으로 인한 변위를 정확하게 계산할 수 있습니다.

d =  vᵢ*t + (a*t²)/2

where:
  d  = displacement
  v = initial velocity
  a  = acceleration
  t  = elapsed time

breakdown:
  vᵢ*t     = movement due to the initial velocity
  (a*t²)/2 = change in movement due to acceleration throughout the frame

따라서이 업데이트 논리를 적용하면

position += (velocity * elapsedTime) + (acceleration * elapsedTime * elapsedTime / 2)
velocity += acceleration * elapsedTime

다음과 같은 결과가 나타납니다.

여기에 이미지 설명을 입력하십시오


2
이것은 유용한 정보이지만 실제로 해당 코드를 어떻게 처리합니까? 첫째, 프레임 속도가 증가함에 따라 오류가 급격히 감소하므로 60 ~ 200fps의 차이는 무시할 수 있습니다 (무한대 대비 8fps는 이미 12.5 %에 불과합니다). 둘째, 스프라이트가 최고 속도에 도달하면 가장 큰 차이는 0.5 단위입니다. 첨부 된 .gif와 같이 실제 보행 속도에는 영향을 미치지 않습니다. 그들이 돌아올 때, 가속은 순간적으로 보입니다 (60+ fps에서 몇 프레임 일 수 있지만 전체 초는 아님).
MichaelS

2
그것은 수학 문제가 아닌 Unity 또는 코드 문제입니다. 빠른 스프레드 시트에 a = 1, vi = 0, di = 0, vmax = 1을 사용하면 t = 1에서 vmax를 d = 0.5로 설정해야합니다. 5 프레임 이상 (dt = 0.2), d (t = 1) = 0.6 50 개가 넘는 프레임 (dt = 0.02), d (t = 1) = 0.51. 500 개가 넘는 프레임 (dt = 0.002), d (t = 1) = 0.501. 따라서 5fps는 20 %, 50fps는 2 %, 500fps는 0.2 %입니다. 일반적으로 오류는 100 / fps 퍼센트가 너무 높습니다. 50fps는 500fps보다 약 1.8 % 높습니다. 그리고 그것은 가속 중입니다. 속도가 최대에 도달하면 차이가 없습니다. a = 100이고 vmax = 5이면 차이가 훨씬 적어야합니다.
MichaelS

2
실제로 VB.net 앱 (dt의 1/60 및 1/200 시뮬레이션)에서 코드를 사용하여 프레임 626 (10.433) 초에 바운스 : 5프레임 2081에서 바운스 : 5 ( 10.405) 초 . 60fps에서 0.27 % 더 많은 시간.
MichaelS

2
10 %의 차이를주는 "운동 학적"접근 방식입니다. 전통적인 접근 방식은 0.27 % 차이입니다. 방금 잘못 표시했습니다. 속도가 최대가 될 때 가속을 잘못 포함했기 때문이라고 생각합니다. 프레임 속도가 높을수록 프레임 당 오류가 줄어들 기 때문에보다 정확한 결과를 얻을 수 있습니다. 당신은 필요합니다 if(velocity==vmax||velocity==-vmax){acceleration=0}. 그러면 프레임 가속의 어떤 부분이 끝났는지 정확히 알지 못하기 때문에 완벽하지는 않지만 오류는 실질적으로 떨어집니다.
MichaelS

6

단계를 어디에서 부르는지에 따라 다릅니다. Update에서 호출하는 경우 Time.deltaTime으로 크기를 조정하면 실제로 프레임 속도와 무관하지만 고정 업데이트에서 호출하면 Time.fixedDeltaTime으로 크기를 조정해야합니다. FixedUpdate에서 단계를 호출한다고 생각하지만 Time.deltaTime으로 확장하면 Unity의 고정 단계가 기본 루프보다 느릴 때 겉보기 속도가 느려집니다. 이는 독립 실행 형 빌드에서 발생합니다. 고정 단계가 느리면 fixedDeltaTime이 큽니다.


1
LateUpdate에서 호출됩니다. 명확하게하기 위해 질문을 업데이트하겠습니다. Time.deltaTime호출 된 위치에 관계없이 여전히 올바른 값을 사용 한다고 생각하지만 (FixedUpdate에서 사용되는 경우 fixedDeltaTime을 사용합니다).
Cooper
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.