Zobacz także tę odpowiedź .
Istnieją dwa typowe sposoby użycia Lerp
:
1. Liniowe mieszanie między początkiem a końcem
progress = Mathf.Clamp01(progress + speedPerTick);
current = Mathf.Lerp(start, end, progress);
Jest to wersja, którą prawdopodobnie znasz.
2. Wykładnicza łatwość w kierunku celu
current = Mathf.Lerp(current, target, sharpnessPerTick);
Zauważ, że w tej wersji current
wartość pojawia się zarówno jako dane wyjściowe, jak i dane wejściowe. Wypiera start
zmienną, więc zawsze zaczynamy od miejsca, w którym przeprowadziliśmy się przy ostatniej aktualizacji. To daje tej wersji Lerp
pamięci od jednej klatki do drugiej. Następnie z tego ruchomego punktu początkowego przesuwamy ułamek odległości w kierunku target
podyktowanym sharpness
parametrem.
Ten parametr nie jest już „szybkością”, ponieważ zbliżamy się do celu w sposób podobny do Zeno . Gdyby sharpnessPerTick
były0.5
, to przy pierwszej aktualizacji przenieślibyśmy się w połowie drogi do celu. Następnie przy następnej aktualizacji przesunęlibyśmy się o połowę pozostałej odległości (czyli o jedną czwartą początkowej odległości). Następnie w następnym przejdziemy ponownie o połowę ...
Daje to „wykładniczą ulgę”, w której ruch jest szybki, gdy daleko od celu, i stopniowo zwalnia, gdy zbliża się asymptotycznie (choć przy liczbach o nieskończonej precyzji nigdy go nie osiągnie w żadnej skończonej liczbie aktualizacji - dla naszych celów jest to wystarczająco blisko). Jest świetny do ścigania ruchomej wartości docelowej lub wygładzania głośnego sygnału wejściowego przy użyciu „ wykładniczej średniej ruchomej ”, zwykle przy użyciu bardzo małego sharpnessPerTick
parametru, takiego jak 0.1
lub mniejszego.
Ale masz rację, w podanej wyżej linku znajduje się błąd. To nie poprawia deltaTime
we właściwy sposób. Jest to bardzo częsty błąd podczas korzystania z tego stylu Lerp
.
Pierwszy styl Lerp
jest liniowy, więc możemy liniowo dostosować prędkość, mnożąc przez deltaTime
:
progress = Mathf.Clamp01(progress + speedPerSecond * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);
Ale nasze łagodzenie wykładnicze jest nieliniowe , więc pomnożenie naszego sharpness
parametru przez deltaTime
nie da poprawnej korekty czasu. Będzie to widoczne jako drżenie w ruchu, jeśli nasze tempo klatek będzie się zmieniać, lub zmiana łagodnej ostrości, jeśli konsekwentnie będziesz przechodził z 30 do 60.
Zamiast tego musimy zastosować wykładniczą korektę dla naszej wykładniczej łatwości:
blend = 1f - Mathf.Pow(1f - sharpness, Time.deltaTime * referenceFramerate);
current = Mathf.Lerp(current, target, blend);
Oto referenceFramerate
po prostu stałe 30
zachowanie jednostek sharpness
tak, jak używaliśmy przed korektą czasu.
W tym kodzie jest jeszcze jeden możliwy do uzasadnienia błąd Slerp
- sferyczna interpolacja liniowa jest przydatna, gdy chcemy dokładnie spójnego tempa obrotu w całym ruchu. Ale jeśli i tak będziemy korzystać z nieliniowej łatwości wykładniczej, Lerp
da to prawie nie do odróżnienia wynik i jest tańszy. ;) Quaternions lerp są znacznie lepsze niż macierze, więc jest to zwykle bezpieczna zamiana.