Jump to content

návod Easing funkce hravě a jednoduše


ffredyk

Recommended Posts

  • Majitel

Zdravíčko přátelé - možná jste někdy ve svém módu řešili animace, interpolaci, či jakýkoliv plynulý pohyb předmětu či čehokoliv jiného. Dá se samozřejmě přes lineární konverzi (jejíž algoritmus najdeme ve všech jazykových podobách na internetu) zařídit ostrý pohyb z bodu A do bodu B. Pokud ale chceme zajistit plynulejší pohyb s rozjezdem a dojezdem - tedy zrychlením a zpomalením, které nebije tolik do očí - je potřeba upravit chování naší lineární konverze tak, aby buď rovnou pracovala na algoritmu, který toto bere v potaz, případně můžeme toto nasimulovat pomocí nástrojů nám svěřených.

Nejdříve si ukážeme "hack" verzi - tedy pro mě jako absolutního nematikáře - absolutní začátek a experiment celé pseudofunkce. Příklad si uvedeme ve hře Arma 3, pro pohyb kamery v cutscéně. Arma 3 nám nabízí funkce pro lineární pohyb kamery z bodu A do bodu B - ostrou cestou. Tím je myšleno, že po spuštění funkce se kamera fixní rychlostí přesune z jejího dosavadního bodu do bodu zadaný v parametru funkce v čase také zadaném v parametru funkce:

_cam = "camera" camCreate [0,0,0]; //Vytvořit kameru na pozici [0,0,0]
_cam cameraEffect ["internal","back"]; //Přepnout obraz hráče do této kamery

_cam camPreparePos [10,10,10]; //připravit přesun na pozici [10,10,10]
_cam camCommitPrepared 5; //přesun trvá 5s

Vizuálně takový přesun je sice použitelný, nicméně není plynulý a strašně neuhlazený (hlavně při pohybu na více bodů v řadě).

Double LERP

Ukážeme si ten nejjednodušší způsob, který vymyslí hlavně nematikář - za pomocí lineárního pohybu, který arma nabízí, můžeme nasimulovat rozjezd a dojezd pomocí zdvojeného lerpování (linearní interpolace, kterou obstará příkaz camCommitPrepared).

//Používáme globální proměnné pro zviditelnění kamer pro skript na EachFrame

V_cam = "camera" camCreate [0,0,0]; //Vytvořit kameru na pozici [0,0,0]
V_cam cameraEffect ["internal","back"]; //Přepnout obraz hráče do této kamery

V_camPos = "camera" camCreate [0,0,0]; //Vytvoří kameru využitou na pozadí pro výpočet lineráního přesunu kamery

V_camLerp = addMissionEventHandler ["EachFrame", //Vytvoříme unscheduled environment spuštěný na každém vykresleném snímku
{
  _T_pos = getPosASL V_camPos; //Vezmeme si ASL pozici kamery pro přesun
  _C_pos = getPosASL V_cam; //Vezmeme si ASL pozici vizuální kamery
  _T_pos = vectorLinearConversion [0,1,0.1,_C_pos, _T_pos]; //Provedeme lineární konverzi (lerp) s progressem 0..0.1..1 (toto zajistí efekt akcelerace)
  
  V_cam camPreparePos (ASLToATL _T_pos); //Nastavíme výslednou (interpolovanou) pozici
  V_cam camCommitPrepared 0;
}];

V_camPos camPreparePos [10,10,10]; //připravit přesun na pozici [10,10,10]
V_camPos camCommitPrepared 5; //přesun trvá 5s

sleep 5.1; //Počkáme 5s na přesun kamery + 0.1s na dokončení akcelerace naší vizuální kamery
removeMissionEventHandler ["EachFrame", V_camLerp]; //Zničíme lerpovací skript

Můžeme si samozřejmě napsat kompletní funkci, která využije příkaz vectorLinearConversion dvakrát a obejde tím rovnou vytvoření druhé kamery a příkaz camCommitPrepared pro přesouvací kameru. Pro jednoduchost a čitelnost skriptu (a variabilitu) jsem předal funkci takto :) 

Pro práci s pohybem kamery (resp. pro relevantní výpočty a manipulace) využívám výhradně pozici ve tvaru ASL (Above Sea Level), protože při interpolaci ATL (Above Terrain Level) kamera při přesunu skáče dle toho jestli pod sebou zrovna nemá kopeček, či díru :) (proto zjišťuji polohy ASL a poté překládám do ATL při nastavení pozice samotné kamery - příkaz pracuje s ATL pozicemi a sám interpoluje na ASL)

Interpolace pomocí algoritmu

Tou "správnou" cestou by měla být pravá matematická interpolace se zahrnutím easingu už v algoritmu. Existuje mnoho různých algoritmu na ty samé pohyby - ať už jde jen o zrychlení, jen o zpomalení, či oboje - na vše je několik variant. Ukážeme si jenom tu základní - na zbytek uvádím silný zdroj: http://gizma.com/easing/

_posA = [0,0,0]; //Nastavení bodů
_posB = [10,10,10];

_startTime = time; //Nastavení výchozího času a délky průběhu
_endTime = _startTime + 5; //+5s

_eh = addMissionEventHandler ["EachFrame", 
{
  _progress = linearConversion [_startTime, _endTime, time, 0, 1, true]; //Převod průběhu času na range 0..1 s clamp
  _progress = _progress * _progress * (3 - 2 * _progress); //https://www.wolframalpha.com/input/?i=x+*+x+*+%283+-+2+*+x%29 (křivka pro easing-inout)
  
  _curPos = vectorLinearConversion [0,1,_progress, _posA, _posB]; //Vlastní výpočet bodu na trase (interpolace s easingem)
}];

sleep 5; //Celý pohyb i s akcelerací bude probíhat přesně 5s
removeMissionEventHandler ["EachFrame", _eh]; 

Kdo chce bejt absolutní fajnšmejkr, tak tu má ještě funkci pro lineární interpolaci (i když ji skoro každej engine i samotný jazyk nabízí už v nějaké matematické třídě)

func_myLerp = 
{
  params ["_A", "_B", "_progress"]; //Parametry funkce
  _progress = _progress max 0 min 1; //Clamp na range 0..1
  
  (_A + (_B - _A) * _progress);
};

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...