Az egyenletes változás szimulálása
Láttuk, hogyan tudunk egyenletes mozgást szimulálni. Mindez valahogy így történt:
A fenti kódot és a Time.deltaTime-mal való szorzás szerepét azért fontos megérteni, mert sokkal tágabb körben használható, mint pusztán az egyenletes mozgás megvalósítása.
Bármilyen egyenletes változást a játékban hasonlóan programozhatunk le:
void Update()
{
// Valami gyenletes sebességgel forog:
transform.Rotate(eulerAngles * angularSpeed * Time.deltaTime);
// Egyenletes sebességgel növeljük az életünk
health += healingSpeed * Time.deltaTime;
// Egyenletes sebességgel fogy az időnk a GameOver előtt.
timeLeft -= Time.deltaTime;
} Általános Vektormennyiség egyenletes változására:
Új vektormennyiség
Eredeti vektormennyiség
Irányvektor (egység hosszú)
Vektor absz. értéke / hossza (skalár, mértéke: valami/másodperc)
Δt Eltelt idő
Az egyenletesen gyorsuló változás szimulálása
Ha gyorsuló mozgást szeretnénk szimulálni, még akkor is történik egyenletes változás. Ebben az esetben maga a sebesség változik egyenletes módon: Ha gyorsulunk, akkor a sebesség egyenletesen nő, ha lassulunk akkor a sebesség egyenletesen csökken.
(Persze nem minden gyorsulás és lassulás egyenletes, de mi egyelőre ezzel foglalkozunk csak.)
Nézzünk egy példát egyenletesen változó mozgásra, ahol …
- Az X tengelyen mozgunk.
- A gyorsulás mértékét egy
[SerializeField]beállítás határozza meg. - Azt pedig, hogy gyorsulunk-e és ha igen, melyik irányba a felhasználói input adja.
Nem sokban különbözik a feladat, ha a gyorsulás iránya a 2D sík vagy 3D tér bármelyik iránya lehet:
Ebben az esetben a sebesség és a gyorsulás egy vektor, tehát nem egy float értéket tárolunk el, hanem egy Vector2 vagy Vector3 típusú sebesség-vektort velocity néven.
A lenti példában a haladás irányát az adott test lokális előre iránya adja meg.
A fenti példában tehát már kettő érték is változhat egyenletesen: A pozíció és a sebességvektor.
Hasonlóan működik az alábbi játék. Próbáld ki! (Irányítás: ◀️🔼▶️)
A fenti kód persze nem mindent valósít még meg. A főbb különbségek:
- Tudunk egyenletesen forogni (Lásd fent: Az egyenletes változás szimulálása)
- Automatikusan lassulunk (Lásd lentebb: Közegellenállás szimulálása)
- Át tudunk menni a falon. (Ezzel most nem foglalkozunk)
- ⚠ Nem az
Updatemetódusban végzem a mozgást. Most következik, hogy hol és miért ott.
A FixedUpdate metódus
Új taggal bővül a Unity által automatikusan hívott MonoBehaviour üzenet metódusok listája.
Ahogy az Update és a LateUpdate, úgy a FixedUptate metódus is Play módban fut le ciklikusan nagy gyakorisággal. Az Update-ről és a LateUpdate-ről azt tanultuk, hogy minden képfrissítés előtt futnak le egyszer-egyszer.
Mivel a képfrissítés sebessége a számítógép teljesítményétől függő, ami még egy adott gépen is folyamatosan változik, ezért az, hogy ezek a metódusok milyen gyakran futnak le egy másodperc alatt az változó. Ezzel szemben a FixedUpdate garantáltan fix időközönként lesz végrehajtva.
FixedUpdate() metódusOlyan MonoBehaviour üzenetmetódus, ami minden képernyőfrissítés előtt fut le automatikusan a play mód alatt.
Az alapbeállítás új projekt esetén két FixedUpdate végrehajtás között eltelt időre: 0.02 másodperc.
Ez azt jelenti, hogy másodpercenként pontosan 50-szer fog lefutni a FixedUpdate egy komponensre. Ezt az értéket a a Projekt beállítások között lehet módosítani:
Felső menüsáv / Edit / Projekt Settings / Time / Fixed Timestep (Érték másodpercben értendő)
Ugyanez az érték elérhető kódból is a Time.fixedDeltaTime -mal.
Ha folyamatos változást szimulálunk FixedUpdate-ben azt hasonlóan tesszük, mint ahogy az Update metódusban, csupán a Time.deltaTime helyett Time.fixedDeltaTime -mal kell szoroznunk.
A 3 Update függvény tehát nagyon hasonló működésű. Az, hogy az Update és LateUpdate közt mi a különbség, arról, itt írtunk: Az idő múlása & Update metódus - A LateUpdate metódus. A következőkben azt tárgyaljuk, hogy mikor használjunk Update-et és mikor FixedUpdate-et?
Update vagy FixedUpdate?
Ha egy grafikonon ábrázoljuk egy test mozgását, úgy, hogy a vízszintes tengelyen látjuk az időt míg a függőleges tengelyen az aktuális sebességet, akkor a függvény alatti terület mérete (azaz a függvény integráltja) adja az elmozdulás mértékét.
A lenti sebesség idő diagram egy egyenletesen gyorsuló mozgást ábrázol.
Az első eset az egyenletes gyorsulás valódi világ beli grafikonját mutatja, a második és a harmadik pedig szimulált gyorsulást, a harmadikat fele akkora frissítési sebességgel végezzük.
A színezett rész mutatja az utat,a mit megtesz a mozgó test. Jól látható, hogy a szimuláció sebessége befolyásolja a megtett út mennyiségét.
Ha a gyorsulás szimulálását az Update metódusban intéznénk akkor a különböző sebességgel futó számítógépeken különböző méretű megtett utakat kapnánk. Ezt elkerülendő használjuk a FixedUpdate-ot.
Mindez egyenletes mozgásnál nem jelent problémát, mivel ebben az esetben a sebesség-idő diagramm egy vízszintes egyenest ír le, így a megtett út különböző frissítési sebesség esetén is egyenlő lesz.
Sőt a pozíció frissítését érdemes az Update-ben intézni, hiszen…
- Ha a
DeltaTime>FixedDeltaTime, a magas FrameRate ellenére a mozgás simaságát le fogja korlátozni a Fixed TimeStep mértéke. - Ha a
FixedDeltaTime>DeltaTime, fölösleges munkát végzünk, azzal, ha a pozíciót aFixedUpdate-ben frissítjük.
A fenti logika kiterjeszthető nem csak a mozgásra (pozíció változására), de bármilyen mennyiség változására. Vezessük be a következő fogalmakat:
- Első szintű változás: Egy mennyiség egyenletes változása.
- Második szintű változás: Egy mennyiség változásának egyenletes változása.
Pl.: Pozíció változása = Egyenletes mozgás
Pl.: Sebesség (pozíció változás) változása = Egyenletesen gyorsuló mozgás
Update-ben kezelni, míg a második szintű változást a FixedUpdate-ben,Inputkezelés és FixedUpdate
Kétféle Inputot kérhetünk le a Unity Input rendszerétől: Pillanatszerű és folyamatos.
- Pillanatszerű: up és down végű metódusok pl.:
Input.GetKeyDownInput.GetMouseButtonUp… - Folyamatos: pl.:
Input.GetKeyInput.GetMouseButton…
Folyamatos Input-ot nyugodtan lekérhetünk bárhol Update-ban és FixedUpdate-ban is, ám pillanatszerű inputot nem szabad FixedUpdate-ben lekérni, csak az Update ciklusban: tehát az Update-ben és a LateUpdate-ben.
A pillanatszerű események pontosan a következő Update ciklusig lekérdezhetők. A következő Update ciklus utolsó LateUpdate metódusának lefutása után a Unity automatikusan visszaállítja az értéküket false-ra. Emiatt nem biztos, hogy a FixedUpdate pontosan ”eltalálja” azt.
Lehetséges hogy néha pontosan jól működik a program, mikor az Time.deltaTime és a Time.fixedDeltaTime körül-belül megegyezik, de az is lehet, hogy:
A - Ha a Time.fixedDeltaTime kisebb, mint a Time.deltaTime, akár több FixedUpdate-en keresztül is igazzal térhet vissza a pillanatszerű lekérdezés. Ekkor tapasztalhatunk például olyasmit, hogy néha dupla akkorát ugrik a karakter.
B - Ha a Time.deltaTime kisebb, mint a Time.fixedDeltaTime, nem mindegyik pillanatszerű lekérdezés lesz észlelhető a FixedUpdate-ben, mert könnyen előfordulhat, hogy elmulasztunk egyet-kettőt.
Az is előfordulhat, hogy Editor-ban fejlesztés alatt az (A) eset igaz, de mikor elkészítjük a build-et, akkor a (B). Mivel a build jóval optimálisabb, mint az Editor béli futás, ezért az Update-ek jóval gyakrabban történnek meg. Ekkor tapasztalhatjuk azt, hogy Editorban minden a lehető legnagyobb rendben volt, build-ben pidig olyan mintha nem venné ben az inputot a játék.
Közegellenállás szimulálása
A közegellenállási erő a haladás irányával ellentétes irányba hat, ezért mindig lassító hatást fog kifejteni. A valóságban mindez különösen bonyolultan számítható. Még az egyszerűsített modellekben is a közegellenállást befolyásolja a test alakja, a haladás irányára merőleges felület mérete és a közeg sűrűsége. Ezzel szemben a közegellenállás szimulálása játékokban nem szokott pontos fizikai modellt követni. A gyakorlatban jóval egyszerűbben oldjuk ezt meg: csupán egy szorzóértékkel.
A Unity fizikában is megvalósított közegellenállást a következőképp tudnánk manuálisan szimulálni:
void FixedUpdate()
{
Vector3 dragVector = -velocity * drag;
velocity += dragVector * Time.fixedDeltaTime;
// Vagy ugyanaz valamivel gyosabban (kicsit kevesebb számítással):
velocity *= 1f - (drag * Time.fixedDeltaTime);
}Kiemelnék pár különbséget a fenti, Unity szimulált fizika, valamint a valódi közegellenállás közt.
- A szimulációban a közegellenállás ereje egyenesen arányos a sebesség mértékével, míg a valóságban a kapcsolat négyzetes.
- A valódi fizikában a test alakja és a haladás irányával merőleges felület befolyásolja a közegellenállás mértékét. Ezeket itt nem vesszük számba, így a papírlap vékony test pont úgy fog zuhanni, mint a gömb.
- A valóságban a gravitációs erő arányos a test tömegével, de a vele ellentétes irányba ható közegellenállási erő nem, ezért a ugyanabban a szituációban a nagyobb tömegű testek jobban tudnak gyorsulni
A pingpong labda a Földön lassabban zuhan, mint az azonos méretű és alakú vas csapágygolyó.
Ezzel szemben a fenti és a Unity beépített fizikai szimulációban ez a hatás nem jelenik meg.