Смотрите также этот ответ .
Есть два распространенных способа использования Lerp
:
1. Линейное смешивание между началом и концом
progress = Mathf.Clamp01(progress + speedPerTick);
current = Mathf.Lerp(start, end, progress);
Это версия, с которой вы, вероятно, больше всего знакомы.
2. Экспоненциальная легкость по отношению к цели
current = Mathf.Lerp(current, target, sharpnessPerTick);
Обратите внимание, что в этой версии current
значение отображается как выход и вход. Она смещает start
переменную, поэтому мы всегда начинаем с того места, куда переместились при последнем обновлении. Это то, что дает этой версии Lerp
памяти от одного кадра к другому. Из этой движущейся начальной точки мы затем перемещаем часть расстояния в направлении, target
определяемом sharpness
параметром.
Этот параметр больше не является «скоростью», потому что мы приближаемся к цели в стиле Zeno . Если бы sharpnessPerTick
были 0.5
, то при первом обновлении мы бы пошли на полпути к нашей цели. Затем в следующем обновлении мы переместим половину оставшегося расстояния (то есть четверть нашего начального расстояния). Тогда на следующем мы двинулись бы снова наполовину ...
Это дает «экспоненциальное замедление», когда движение происходит быстро, когда оно далеко от цели, и постепенно замедляется по мере асимптотического приближения (хотя с числами бесконечной точности оно никогда не достигнет его в любом конечном количестве обновлений - для наших целей это подбирается достаточно близко). Это отлично подходит для погони за движущимся целевым значением или для сглаживания шумового ввода с использованием « экспоненциального скользящего среднего », обычно с использованием очень маленького sharpnessPerTick
параметра, например 0.1
или меньше.
Но вы правы, есть ошибка в ответе, на который вы ответили. Это не deltaTime
правильно для правильного пути. Это очень распространенная ошибка при использовании этого стиля Lerp
.
Первый стиль Lerp
линейный, поэтому мы можем линейно регулировать скорость, умножая на deltaTime
:
progress = Mathf.Clamp01(progress + speedPerSecond * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);
Но наше экспоненциальное ослабление нелинейно , поэтому умножение нашего sharpness
параметра на deltaTime
не даст правильной коррекции времени. Это будет проявляться как дрожь в движении, если наша частота кадров колеблется, или изменение резкости ослабления, если вы последовательно переходите от 30 до 60.
Вместо этого нам нужно применить экспоненциальную коррекцию для нашей экспоненциальной простоты:
blend = 1f - Mathf.Pow(1f - sharpness, Time.deltaTime * referenceFramerate);
current = Mathf.Lerp(current, target, blend);
Вот referenceFramerate
только константа, как 30
сохранить единицы для sharpness
того же, что мы использовали, прежде чем исправлять на время.
В этом коде есть еще одна спорная ошибка Slerp
- сферическая линейная интерполяция полезна, когда мы хотим точно одинаковую скорость вращения на протяжении всего движения. Но если мы все равно будем использовать нелинейную экспоненциальную простоту, Lerp
это даст почти неразличимый результат и будет дешевле. ;) Кватернионы выглядят гораздо лучше, чем матрицы, поэтому обычно это безопасная замена.