Search
Write a publication
Pull to refresh

Comments 21

ИМХО, в данном примере, это баг в реализации функции Update, имеет мало отношения к самой функции Lerp.

Это не баг. Без сохранения состояния, в итерациях, функции принимающие время жизни как параметр не могут работать корректно. Грубо говоря, от балды задали параметры, от балды получили результат. Этот лерп надо или в корутину класть или хранить состояние руками.

Ну именно это и есть баг – в широком смысле, как "некорректное поведение". Для корректного поведения там надо не дельту между кадрами в качестве параметра передавать, а, хотя бы, дельту с момента начала перемещения (которое, да, надо будет где-то хранить).

Например, будет накопление ошибки. Особенно если объект находится далеко от нуля и едет медленно. Он просто не будет попадать в конечную точку. В некоторых случаях он может тупо застрять на полпути потому что приращение будет меньше эпсилон.

Это самый быстрый способ сделать движение, сойдет для простеньких тестовых проектов.
Для реальных задач это сочетание update+lerp для движения лучше не использовать.
Другой вопрос, который уже задал я сам себе: зачем тогда умножать на Time.deltaTime в методе FixedUpdate()?
Потому, что интервал вызова FixedUpdate задается в настройках Юнити, и не является константов в глобальном смысле слова.
Лерп абсолюнто имеет право на жизнь. Просто надо интерполировать не между текущей и конечной точками, а сначала запомнить начальную (которая в начале пути равна текущей) и интерполировать между начальной и конечной, тогда движение будет линейным.
У меня больше другое вызывает вопросы — зачем для Lerp статья на хабре? Приведенное в статье использование оной не является багой или фичей, о которой никто не знает — а всего лишь один из вариантов реализации, и поведение его вполне объяснимо и предсказуемо. Так что я немного удивился.

В данном примере lerp по дельте между кадрами между начальной и конечной точкой не даст вообще никакого движения. Максимум – получится дёрганье на месте.

Vector3 destinationPoint;
Vector3 startPoint;
float time;
...
void Start()
{
    startPoint = transform.position;
}

void Update()
{
    transform.position = Vector3.Lerp (startPoint, destinationPoint, time);
    time += Time.deltaTime;
    if(time > 1)
    {
        time = 0;
    }
}


как вариант. будет клавно идти от начала до конца, каждый раз возвращаясь в начало.
time += Time.deltaTime;

я вот об этом как раз и говорил :-)
В примере из статьи напрямую Time.deltaTime используется в качестве параметра.

Или использовать Time.time. Но за 8 лет работы с Юнити подобными конструкциями приходилось пользоваться разве что в самом начале, сейчас это в основном iTween, LeenTween и, конечно, корутины. Вышеприведенный код больше образовательного формата.
Вы говорите как один из моих знакомых )) Собственно, поэтому я и написал статью. Несмотря на малый размер кода — действие не такое очевидное.
Код работает. Движение происходит от начальной точки к конечной, с плавным замедлением. А вот предложенный выше в комментарих вариант — это уже истинная линейная интерполяция.

Ваш пример работает только потому, что на каждый Update меняется начальная точка лерпа, как вы и написали в посте. А Tutanhomon в комментарии выше предложил начальную точку зафиксировать. В этом случае движения как раз не будет.

А, простите. Думал, Вы говорите про пример из статьи. Прошу прощения.
Как показывает моя практика общения (и комментарии к статье) — поведение не особо предсказуемо. Собственно, потому и статья.
Делать нелинейную интерполяцию лучше твинером (HOTween или DOTween например).

"популярный шаблон ее использования"
Что делает функция Lerp можно понять (да и надо бы знать) по первой ссылке в гугле. Как программист, я бы сразу задал себе 2 вопроса:


  • Зачем передавать каждый раз текущие координаты?
  • Зачем передавать Time.deltaTime?

По мне так самый адекватный ответ — сломать себе мозг и получить трудно представимое поведение. Как только узнал про такую функцию (еще где-то до юнити), юзал ее так, как она и предполагает — как написал в комментарии Tutanhomon.
Поэтому я бы с ПОПУЛЯРНОСТЬЮ такого использования бы поспорил. Имхо, адекватные люди так не пишут код.


Вот кстати, если вы такую статью большую написали, сделали бы график нелинейного движения, написали бы закон изменения координат) Исследование настоящее)

Касательно популярности. Собственно, практически _все_ туториалы с официального сайта (https://unity3d.com) используют данный шаблон.

Что делает функция Lerp _сама по себе_ — это как раз понятно. И именно эта понятность и затрудняет понимание вышеприведенного кода, где результат уже не линеен. Именно поэтому и статья.

А «график» приведенного движения довольно прост: при стремлении временного интервала к нулю получаем классическую exp(-k*t), где k — некий коэффициент. Собственно, это видно из базового разностного уравнения.

Если подумать, вопрос был даже не в использовании функции так или иначе (в конечном итоге программист сам решает, как ему использовать имеющиеся инструменты). Вопрос, скорее, в том, что в этих самых туториалах раз за разом дается такая форма без объяснения. Что вызывает у людей вопросы. Или не вызывает, в зависимости от.
Для FixedUpdate() заведена своя переменная fixedDeltaTime, которая, судя по названию, должна давать время между его вызовами… Но нет же, сами же разработчики рекомендуют и в FixedUpdate() и в Update() использовать deltaTime, поскольку частота вызовов FixedUpdate() фиксированная-фиксированная, да не очень.

Time.deltaTime — время в секундах затраченное на отображение последнего фрейма, после отображения предпоследнего.
Time.fixedDeltaTime — интервал в секундах, через каждый промежуток fixedDeltaTime вызывается обновление физики и другие фиксированные апдейты, как, например, fixedUpdate()

Также есть момент, что deltaTime вернет вроде бы fixedDeltaTime, если будет использован в fixedUpdate()
Промежутки между вызовами fixedUpdate() фиксированы, но их можно менять для увеличения/уменьшения точности расчета физики.

Еще звучал интересный вопрос, который я не совсем понял: коль скоро значение параметра интерполяции не меняется, зачем там вообще deltaTime?

Для того, чтобы скорость игры оставалась постоянной для любой машины на которой запущена игра.
Пример: если нет deltaTime, то при 100fps и 10fps объект переместиться за разное реальное время, так как в одном случае метод выполнится 100 раз за секунду, а в другом лишь 10.

С deltaTime и fixedDeltaTime в их теоретическом значении — понятно. Просто имеется рекомендация разработчиков использовать и в FixedUpdate(), и в Update() — deltaTime. Что несколько смущает. Так как автоматически означает рекомендацию не использовать fixedDeltaTime.

«Еще звучал интересный вопрос, который я не совсем понял: коль скоро значение параметра интерполяции не меняется, зачем там вообще deltaTime?»
Я не понял вопрос человека, а не ответ. Который, в свою очередь, очевиден.
Sign up to leave a comment.

Articles