A 2D platformer játékunk magja a karakter controller. Ez fogja vezényelni a játékosunk mozgását.
GameObject és komponensek
Mindenek előtt szükségünk van egy GameObject-re. Hozzunk létre egy üreset és nevezzük el Player-nek.
Legcélszerűbb Unity fizikát használni az ütközésdetektálásra, minden mást manuálisan programozunk le.
Adjunk tehát hozzá a GameObject-hez egy Rigidbody2D-t, valamint egy 2D Collider-t, ami a karakter alakját adja meg.
Javaslom, hogy használjunk CapsuleCollider2D-t vagy BoxColluder2D-t.
Mivel teljesen manuálisan szeretnénk szabályozni a játékos működését, hozzunk létre egy PhysicsMaterial2D fájlt. Állítsuk be, hogy a pattanóssága és a súrlódása is nulla legyen és kössük rá a Collider-re. Ezáltal sem pattogni sem a felületi súrlódástól lelassulni nem fog a játékos.
A játékos karaktert meg is kell jeleníteni valahogy a felhasználónak. Erre a célra én most SpriteRenderer-t használok, de nyugodtan lehetett volna MeshRenderer is.
A SpriteRenderer-nek beállítom a Sprite-ját:
Jelen esetben én a Ingyenes Asset-ek közül választottam egy karaktert.
Ne felejtsük el beállítani a Collider méretét a megfelelő képhez.
A működés logikáját mi programozzuk le. Erre a célra létre hozunk egy új MonoBehaviour osztályt Platformer2DController néven és hozzáadjuk a Player GameObject-hez.
A referenciák bekötése a kódban
Kezdjük azzal az osztályunkat, hogy bekötjük a szükséges referenciákat. A komponensnek ismernie kell majd:
Rigidbody2Dkomponenst: Mozgás szabályozása- Visuals
GameObjectTransformkomponensét: Irányba forgatás
Ezek [SerializeField] beállítások. Automatikusan kössük be őket az OnValidate() Unity metódusban.
Figyeljünk arra, hogy valóban a Visuals GameObject legyen bekötve a visuals field-hez. Ha kell módosítsuk manuálisan.
A Rigidbody helyes beállítása
Ezután kódból automatikusan beállíthatjuk a Rigidbody2D-t a SetupRigidbody() metódusban, amit az OnValidate()-ből hívunk. (Ezt a beállítást lehetne manuálisan is végezni az Inspector felületen, de az alábbi megoldás kevesebb esélyt ad későbbi hibák elkövetésére.)
Az alábbiakban végig-veszem miért ezeket a beállításokat választottuk:
- rigidbody.bodyType = RigidbodyType2D.Dynamic
- rigidbody2D.simulated = true;
- rigidbody2D.drag = 0f; rigidbody2D.angularDrag = 0f;
- rigidbody2D.gravityScale = 0f;
- rigidbody2D.interpolation = RigidbodyInterpolation2D.Interpolate;
- rigidbody2D.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
- rigidbody2D.constraints = RigidbodyConstraints2D.FreezeRotation;
Dinamikus Rigidbody szükséges az ütközésdetektáláshoz: Unity fizika és a Rigidbody
Enélkül nem is mozgatná a fizika a karaktert.
Nincs szükségünk semmilyen közegellenállásra.
A gravitációt manuálisan kódból fogjuk szabályozni, ezért kikapcsoljuk az automatikusat.
A fizikai szimuláció framerate ne befolyásolja a megjelenítés sebességét.
Ez a legpontosabb ütközésdetektálás. Lévén a fő karakterről van szó, hasznos a legjobbat használni, még ha kicsikét több erőforrást vesz is igénybe.
Nem akarjuk, hogy a karakter el tudjon forogni, ezért lelockoljuk.
Földön állunk-e?
Állapítsuk meg az aktuális állapotunkat! Éppen állunk valamin vagy a levegőben esünk?
Ezt és még néhány egyéb információ tárolására hozzunk létre field-eket:
// Current State
Collider2D _platform = null; // A platform referenciája, amin állunk
float _groundingStateChangedTime = 0f; // Az állapot megváltozásának ideje
// Egy property-t is létrehozunk, ami megmondja, hogy éppen földön állunk-e az alapján,
// hogy a platform változóban van-e valami érték a null-on kívül.
bool IsGrounded => _platform != null;Az OnCollisionEnter2D és OnCollisionExit2D metódusokat fogunk használni hogy mikor találkozunk és mikor távolodunk el platformtól.
Ezt az egy egyszerű megoldást a talajdetektálásra majd később bővítjük.
Input
Ez a kontroller ugrani, és jobbra mozogni lesz képes. A megfelelő billentyűket bekötjük [SerializeField] változókba.
[Header( "Input" )]
[SerializeField] KeyCode leftKey = KeyCode.A; // Balra gomb
[SerializeField] KeyCode rightKey = KeyCode.D; // Jobbra gomb
[SerializeField] KeyCode jumpKey = KeyCode.Space; // Ugrás gombFolyamatos mozgás szimulálása
A mozgás szimulálását a FixedUpdate( ) Unity MonoBechaviour életciklus metódusban végezzük.
A fenti kódban az aktuális sebességet két komponensre bontjuk: Vízszintes és függőleges. Utána függetlenül számításokat végzünk ezen a két float számon két saját függvény segítségével majd végül visszaírjuk a rigidbody2D.velocity-be.
Vízszintes mozgás
Először is vegyük fel a vízszintes mozgáshoz szükséges beállításokat.
Ezek a mi esetünkben a következők lesznek:
A földön és a levegőben a platformer játékok nagy részében máshogy viselkedik a karakter, ezért mi is különválasztjuk ezt a két esetet.
Utána megírjuk a HandleHorizontal metódust, amit FixedUpdate()-ből hívtunk
HandleHorizontal egy minden FixedUpdate()-ban újra számolja tehát a vízszintes sebességet az aktuális helyzet alapján.
Gravitáció kezelése
Esés beállításai:
[Header( "Vertical Movement" )]
[SerializeField, Min(0)] float gravity = 15f; // Gravitációs gyorsulás
[SerializeField, Min(0)] float maxFallSpeed = 15f; // Max lefelé esési sebességEsés logikáját a HandleVertical metódusban írjuk meg. Emlékezzünk rá, hogy ezt a metódust a FixedUpdate()-ból hívtuk.
float HandleVertical(float y) // Függőleges sebesség kezelése
{
// Gravitáció kezelése
y -= gravity * Time.fixedDeltaTime;
// Maximumális függőleges sebesség kezelése
if (y < -maxFallSpeed)
y = -maxFallSpeed;
return y; //Visszaadjuk a módosított függőleges sebességet
}Az ugrás kezelése
Egyelőre egy beállítás fog tartozni az ugráshoz, a kezdősebesség. Ezt először is vegyük fel [SerializeField]-ként.
[Header( "Jumping" )]
[SerializeField, Min(0)] float jumpStartJumpVelocity = 5f; // Ugrás kezdő sebességeEmlékezzünk, hogy az Input osztály pillanatszerű lekérdezéseit csak az Update és LateUpdate metódusokban érdemes hívni és nem a FixedUpdate-ben. Ellenkező esetben hibás eredményt adhat: Billentyűzet és Gamepad Input
Mivel az ugrás egy pillanatszerű művelet, ezért az Update-ban kezeljük le.
A folyamatos ugrás
TODO
AirJump: Ugrás a levegőben
TODO
Kifinomultabb módja a földet érés megállapításának
TODO
Coyote Time
A kengyelfutó gyalogkakukk című népszerű Warner Bros. rajzfilm egyik főszereplője a prérifarkas vagy eredeti nevén Wile E. Coyote. Akik megfelelő figyelemmel nézték hősünk kezdettől bukásra ítélt cselszövéseit, tudják, hogy a gravitáció leggyakrabban nem akkor kezd el lefelé gyorsítani egy prérifarkast, amikor az alatt nincs talaj, hanem csak akkor amikor az állat lenéz, sőt néha még csak azután hogy felemel egy táblát és pislog kettőt. Igazság szerint a fizika eléggé inkonzisztens a prérifarkasokkal.
Ezért kapta erről az állatról a nevét a platformer játékfejlesztés egy fontos koncepciója, a Coyote Time (Prérifarkas idő)