Почему положение объекта, полученное через Time.deltaTime, отличается от положения, полученного через таймер?

Я пытаюсь перемещать объект не плавно, а «ступенчато». Это означает, что я не использую Time.deltaTime в Update(). Вместо этого через равные промежутки времени я перемещаю объект на некоторое одинаковое расстояние. При этом я ожидаю, что на достаточно большом расстоянии от начала движения не должно быть разницы между способами перемещения. Однако я заметил, что через несколько итераций «пошаговое» перемещение начинает заметно отставать от перемещения с помощью Time.deltaTime.

Изначально я хотел сделать подобие анимированного плоттера с помощью LineRenderer, поэтому и возник вопрос о «ступенчатом» перемещении. Я заметил значительные отставания от ожидаемой скорости графика. В попытке выяснить суть проблемы я начал отбрасывать всё лишнее. В итоге я собрал простую сцену с двумя сферами. Для каждой из них написан похожий скрипт.

public class MoverDeltaTime : MonoBehaviour
{
    private readonly float _step = 0.1f;
    private readonly float _timeout = 0.1f;
    private float _speed;

    private void Start()
    {
        _speed = _step / _timeout;
    }

    void Update()
    {
        /* Здесь _speed * Time.deltaTime - расстояние, пройденное за время Time.deltaTime */
        transform.Translate(_speed * Time.deltaTime * Vector3.left);
    }
}
public class MoverTimed : MonoBehaviour
{
    private readonly float _step = 0.1f;
    private readonly float _timeout = 0.1f;
    private float _lastTime;

    void Update()
    {
        if (Time.time - _lastTime >= _timeout)
        {
            /* Здесь _step - расстояние, пройденное за время _timeout */
            transform.Translate(_step * Vector3.left);
            _lastTime = Time.time;
        }
    }
}

Очевидно, что _speed * Time.deltaTime - это расстояние, пройденное за время Time.deltaTime. Но аналогично и _step во втором скрипте - это расстояние, пройденное за время _timeout. В моем понимании, здесь одинаковый подход. Однако на практике между сферами растет расстояние. Это заметно на скриншоте (камера в ортографическом режиме, поэтому это не искажение).

В связи с этим меня мучают вопросы, почему так происходит? Я допустил какую-то глупую ошибку в рассуждениях? Я неверно понимаю принцип работы Time.deltaTime? Или всё дело в округлениях float-переменных? И как, собственно, правильно перемещать объекты «ступенчато»?

Исходное положение

Через несколько секунд


Ответы (1 шт):

Автор решения: Yaroslav

transform.Translate(_step*Vector3.left); А остаток времени извиняюсь спросить куда девается? Time.time-_lastTime > _step... где разница? типа она ничтожна?

Ты можешь потерять при 60 fps от 0 до 0,016 секунды в момент шага. При шаге раз в 0.1, то есть 10 раз в секунду теряешь по 0,008(среднее), в итоге 0,08 в секунду. Через всего 10 секунды ты потеряешь 0,8, почти целую секунду.

public class Move : MonoBehaviour
{
    [SerializeField] private Vector3 _vector = Vector3.left;

    private void Update ()
    {
        transform.position += _vector*Time.deltaTime;
    }
}

public class MoveStep : MonoBehaviour
{
    [SerializeField] private Vector3 _vector = Vector3.left;
    [SerializeField] private float _timeStep = 0.1f;
    private float _timer;

    // test
    [SerializeField] private Move _move;
    private bool _isChange;
    
    private void Update ()
    {
        _timer += Time.deltaTime;
        if (_timer >= _timeStep) 
        {
            transform.position += _vector*_timeStep;
            _timer -= _timeStep;
            _isChange = true;
        }
    }

    // в конце кадра, когда Update обоих уже сработал
    private void LateUpdate ()
    {
        if (_move != null && _isChange)
        {
            Debug.Log("M  "+_move.transform.position);
            Debug.Log("MS "+transform.position);
            _isChange = false;
        }
    }
}
→ Ссылка