V tomto článku pokračujeme vo vývoji ovládača postavy pre 2D plošinovku v Unity, pričom dôkladne skúmame každý krok konfigurácie a optimalizácie ovládacích prvkov. V predchádzajúcom článku „ “ sme podrobne rozobrali, ako vytvoriť základy postavy, vrátane jej fyzického správania a základného pohybu. Teraz je čas prejsť k pokročilejším aspektom, ako je manipulácia so vstupmi a dynamické sledovanie kamery. Ako vytvoriť 2D ovládač postavy v Unity: Časť 1 V tomto článku sa ponoríme do nastavenia nového vstupného systému Unity, vytvárania aktívnych akcií na ovládanie postavy, umožňuje skákanie a zabezpečuje správne reakcie na príkazy hráča. Ak chcete sami implementovať všetky zmeny opísané v tomto článku, môžete si stiahnuť vetvu úložiska „ “, ktorá obsahuje základ pre tento článok. Prípadne si môžete stiahnuť vetvu „ “ s konečným výsledkom. Character Body Character Controller Nastavenie vstupného systému Predtým, než začneme písať kód na ovládanie našej postavy, musíme v projekte nakonfigurovať vstupný systém. Pre našu plošinovku sme si vybrali nový vstupný systém Unity, ktorý bol predstavený pred niekoľkými rokmi, ktorý zostáva relevantný vďaka svojim výhodám oproti tradičnému systému. Vstupný systém ponúka modulárnejší a flexibilnejší prístup k manipulácii so vstupmi, čo umožňuje vývojárom jednoducho nastaviť ovládacie prvky pre rôzne zariadenia a podporovať zložitejšie vstupné scenáre bez dodatočnej réžie implementácie. Najprv nainštalujte balík Input System. Otvorte Správcu balíkov z hlavnej ponuky výberom položky Okno → Správca balíkov. V sekcii Unity Registry nájdite balík "Vstupný systém" a kliknite na "Inštalovať". Ďalej prejdite do nastavení projektu cez ponuku Upraviť → Nastavenia projektu. Vyberte kartu Player, nájdite časť Active Input Handling a nastavte ju na „Input System Package (New). Po dokončení týchto krokov vás Unity vyzve na reštart. Po reštarte bude všetko pripravené na konfiguráciu ovládacích prvkov pre nášho kapitána. Vytváranie vstupných akcií V priečinku vytvorte Vstupné akcie cez hlavné menu: . Pomenujte súbor „Ovládacie prvky“. Nastavenia Majetok → Vytvoriť → Vstupné akcie Unity's Input System je výkonný a flexibilný nástroj na správu vstupov, ktorý umožňuje vývojárom konfigurovať ovládacie prvky pre postavy a herné prvky. Podporuje rôzne vstupné zariadenia. Akcie vstupu, ktoré vytvoríte, poskytujú centralizovanú správu vstupov, zjednodušujú nastavenie a robia rozhranie intuitívnejším. Dvakrát kliknite na súbor , aby ste ho otvorili na úpravy, a pridajte pre ovládanie postavy s názvom „Character“. Controls mapu akcií Akčná mapa v Unity je súbor akcií, ktoré možno prepojiť s rôznymi ovládačmi a klávesmi na vykonávanie konkrétnych úloh v hre. Je to efektívny spôsob organizácie ovládacích prvkov, ktorý umožňuje vývojárom prideľovať a upravovať vstupy bez prepisovania kódu. Ďalšie podrobnosti nájdete v oficiálnej . dokumentácii vstupného systému Prvá akcia sa bude nazývať „Presunúť“. Táto akcia určí smer pohybu postavy. Nastavte na "Value" a na "Vector2", aby ste umožnili pohyb v štyroch smeroch. Action Type Control Type Priraďte k tejto akcii väzby výberom Pridať hore/nadol/vpravo/vľavo kompozit a priraďte známe klávesy WASD k ich príslušným smerom. Nezabudnite uložiť nastavenia kliknutím na položku . Toto nastavenie zaisťuje, že môžete zmeniť priradenie väzieb pre akciu „Presunúť“, napríklad klávesám so šípkami alebo dokonca joysticku gamepadu. Uložiť aktívum Potom pridajte novú akciu – „Skočiť“. ponechajte ako „Tlačidlo“, ale pridajte novú – „Stlačte“ a nastavte na „Stlačte a uvoľnite“, pretože potrebujeme zachytiť stlačenie aj uvoľnenie tlačidla. Typ akcie interakciu Správanie spúšťača Tým je schéma ovládania postavy hotová. Ďalším krokom je napísať komponent, ktorý tieto akcie zvládne. Posúvanie postavy doľava a doprava Nastal čas prepojiť vstupné akcie, ktoré sme vytvorili na ovládanie postavy, s komponentom , čo postave umožní aktívne sa pohybovať po scéne podľa našich ovládacích príkazov. CharacterBody Aby sme to dosiahli, vytvoríme skript zodpovedný za ovládanie pohybu a pre prehľadnosť ho pomenujeme . V tomto skripte najskôr zadefinujeme niektoré základné polia. Pridáme odkaz na komponent , , ktorý bude priamo riadený skriptom. CharacterController CharacterBody _characterBody Taktiež nastavíme parametre pre rýchlosť pohybu postavy ( ) a výšku skoku ( ). Okrem toho definujeme účel poľa . _speed _jumpHeight _stopJumpFactor Možno ste si všimli, že v mnohých 2D plošinovkách je možné ovládať výšku skoku. Čím dlhšie tlačidlo skoku držíte, tým vyššie postava vyskočí. V podstate sa na začiatku skoku použije počiatočná rýchlosť smerom nahor a táto rýchlosť sa zníži, keď sa tlačidlo uvoľní. určuje, o koľko sa zníži rýchlosť nahor po uvoľnení tlačidla skoku. _stopJumpFactor Tu je príklad kódu, ktorý napíšeme: // CharacterController.cs public class CharacterController : MonoBehaviour { [SerializeField] private CharacterBody _characterBody; [Min(0)] [SerializeField] private float _speed = 5; [Min(0)] [SerializeField] private float _jumpHeight = 2.5f; [Min(1)] [SerializeField] private float _stopJumpFactor = 2.5f; } Ďalej implementujeme možnosť pohybu postavy doľava a doprava. Pri podržaní tlačidla pohybu by si postava mala udržiavať určenú rýchlosť pohybu bez ohľadu na prekážky. Aby sme to dosiahli, pridáme do skriptu premennú na uloženie aktuálnej rýchlosti pohybu pozdĺž povrchu (alebo jednoducho horizontálne, keď je postava vo vzduchu): // CharacterController.cs private float _locomotionVelocity; V komponente predstavíme metódu na nastavenie tejto rýchlosti: CharacterBody // CharacterBody.cs public void SetLocomotionVelocity(float locomotionVelocity) { Velocity = new Vector2(locomotionVelocity, _velocity.y); } Keďže naša hra nemá naklonené povrchy, táto metóda je celkom jednoduchá. V zložitejších scenároch by sme museli brať do úvahy stav telesa a sklon povrchu. Zatiaľ jednoducho zachováme vertikálnu zložku rýchlosti a zároveň upravíme iba horizontálnu súradnicu . x Ďalej túto hodnotu nastavíme v metóde na každom snímku: Update // CharacterController.cs private void Update() { _characterBody.SetLocomotionVelocity(_locomotionVelocity); } Definujeme metódu spracovania signálov z akcie Input: Move // CharacterController.cs public void OnMove(InputAction.CallbackContext context) { var value = context.ReadValue<Vector2>(); _locomotionVelocity = value.x * _speed; } Keďže akcia je definovaná ako , kontext poskytne vektorovú hodnotu v závislosti od toho, ktoré klávesy sú stlačené alebo uvoľnené. Napríklad stlačenie klávesu spôsobí, že metóda prijme vektor (1, 0). Súčasné stlačenie oboch a spôsobí (1, 1). Uvoľnením všetkých kláves sa spustí s hodnotou (0, 0). Move Vector2 D OnMove D W OnMove Pre kláves bude vektor (-1, 0). V metóde vezmeme horizontálnu zložku prijatého vektora a vynásobíme ju zadanou rýchlosťou pohybu, . A OnMove _speed Naučte postavu skákať Najprv musíme naučiť komponent zvládať skákanie. Aby sme to dosiahli, pridáme metódu zodpovednú za skok: CharacterBody // CharacterBody.cs public void Jump(float jumpSpeed) { Velocity = new Vector2(_velocity.x, jumpSpeed); State = CharacterState.Airborne; } V našom prípade je táto metóda jednoduchá: nastaví vertikálnu rýchlosť a okamžite zmení stav postavy na . Airborne Ďalej musíme určiť rýchlosť, akou má postava skákať. Už sme definovali výšku skoku a vieme, že gravitácia neustále pôsobí na telo. Na základe toho je možné vypočítať počiatočnú rýchlosť skoku pomocou vzorca: kde je výška skoku a je gravitačné zrýchlenie. Zohľadníme aj multiplikátor gravitácie prítomný v komponente . Pridáme nové pole na definovanie počiatočnej rýchlosti skoku a vypočítame ju takto: h g CharacterBody // CharacterController.cs private float _jumpSpeed; private void Awake() { _jumpSpeed = Mathf.Sqrt(2 * Physics2D.gravity.magnitude * _characterBody.GravityFactor * _jumpHeight); } Budeme potrebovať ďalšie pole na sledovanie toho, či postava práve skáče, aby sme vo vhodnom čase mohli obmedziť rýchlosť skoku. Navyše, ak hráč drží tlačidlo skoku až do pristátia, mali by sme túto vlajku resetovať sami. Toto sa vykoná v metóde : Update // CharacterController.cs private bool _isJumping; private void Update() { if (_characterBody.State == CharacterState.Grounded) { _isJumping = false; } //... } Teraz napíšme metódu na spracovanie akcie : Jump // CharacterController.cs public void OnJump(InputAction.CallbackContext context) { if (context.started) { Jump(); } else if (context.canceled) { StopJumping(); } } Keďže akcia je tlačidlo, môžeme z kontextu určiť, či stlačenie tlačidla začalo ( ) alebo skončilo ( ). Na základe toho skok buď iniciujeme alebo zastavíme. Jump context.started context.canceled Tu je spôsob vykonania skoku: // CharacterController.cs private void Jump() { if (_characterBody.State == CharacterState.Grounded) { _isJumping = true; _characterBody.Jump(_jumpSpeed); } } Pred skokom skontrolujeme, či je postava na zemi. Ak áno, nastavíme príznak a prinútime telo skákať pomocou . _isJumping _jumpSpeed Teraz implementujme správanie preskakovania: // CharacterController.cs private void StopJumping() { var velocity = _characterBody.Velocity; if (_isJumping && velocity.y > 0) { _isJumping = false; _characterBody.Velocity = new Vector2( velocity.x, velocity.y / _stopJumpFactor); } } Skok zastavíme, len ak je aktívny príznak . Ďalšou dôležitou podmienkou je, že postava sa musí pohybovať nahor. To zabraňuje obmedzeniu rýchlosti pádu, ak sa tlačidlo skoku uvoľní pri pohybe nadol. Ak sú splnené všetky podmienky, resetujeme príznak a znížime vertikálnu rýchlosť o faktor . _isJumping _isJumping _stopJumpFactor Nastavenie postavy Teraz, keď sú všetky komponenty pripravené, pridajte komponenty a k objektu na scéne. Uistite sa, že ste vybrali komponent , ktorý sme vytvorili, nie štandardný komponent Unity určený na ovládanie 3D postáv. PlayerInput CharacterController Captain CharacterController Pre priraďte existujúci komponent k postave. Pre nastavte predtým vytvorené v poli . CharacterController CharacterBody PlayerInput ovládacie prvky Actions Ďalej nakonfigurujte komponent PlayerInput tak, aby volal príslušné metódy z CharacterController. Rozbaľte sekcie Udalosti a Postavy v editore a prepojte zodpovedajúce metódy s akciami Presun a Skok. Teraz je všetko pripravené na spustenie hry a testovanie toho, ako všetky nakonfigurované komponenty spolupracujú. Pohyb kamery Teraz musíme prinútiť kameru, aby sledovala postavu, kamkoľvek idú. Unity poskytuje výkonný nástroj na správu kamier — . Cinemachine Cinemachine je revolučné riešenie pre ovládanie kamery v Unity, ktoré ponúka vývojárom širokú škálu možností na vytváranie dynamických, dobre vyladených kamerových systémov, ktoré sa prispôsobia herným potrebám. Tento nástroj uľahčuje implementáciu zložitých kamerových techník, ako je sledovanie postavy, automatické nastavenie zaostrenia a mnoho ďalších, čím dodáva každej scéne vitalitu a bohatosť. Najprv nájdite na scéne objekt a pridajte k nemu komponent . Main Camera CinemachineBrain Ďalej vytvorte nový objekt v scéne s názvom . Toto bude kamera, ktorá bude nasledovať kapitána, rovnako ako profesionálny kameraman. Pridajte k nemu komponent . Nastavte pole na kapitána, v poli vyberte a nastavte parameter na 4. CaptainCamera CinemachineVirtualCamera Sledovať Telo Rámovací transponér Lens Ortho Size Okrem toho budeme potrebovať ďalší komponent na definovanie posunu kamery vzhľadom na postavu — . Nastavte hodnotu na 1,5 a hodnotu na -15. CinemachineCameraOffset Y Z Teraz si otestujme, ako kamera sleduje našu postavu. Myslím, že to dopadlo celkom dobre. Všimol som si, že fotoaparát občas mierne zadrhá. Aby som to vyriešil, nastavil som pole Metóda aktualizácie zmesi v objekte hlavného fotoaparátu na FixedUpdate. Zlepšenie skokov Poďme otestovať aktualizovanú mechaniku. Skúste behať a neustále skákať. Skúsení hráči si môžu všimnúť, že skoky nie vždy registrujú. Vo väčšine hier to nie je problém. Ukazuje sa, že je ťažké predpovedať presný čas pristátia na opätovné stlačenie tlačidla skoku. Musíme urobiť hru viac odpúšťajúcou tým, že umožníme hráčom mierne stlačiť skok pred pristátím a postave tak okamžite po pristátí vyskočia. Toto správanie je v súlade s tým, na čo sú hráči zvyknutí. Aby sme to mohli implementovať, zavedieme novú premennú , ktorá predstavuje časové okno, počas ktorého sa môže skok spustiť, ak sa naskytne príležitosť. _jumpActionTime // CharacterController.cs [Min(0)] [SerializeField] private float _jumpActionTime = 0.1f; Pridal som pole , ktoré označuje koniec okna akcie skoku. Inými slovami, kým sa nedosiahne , postava bude skákať, ak sa naskytne príležitosť. Aktualizujme tiež obsluhu akcie . _jumpActionEndTime _jumpActionEndTime Jump // CharacterController.cs private float _jumpActionEndTime; public void OnJump(InputAction.CallbackContext context) { if (context.started) { if (_characterBody.State == CharacterState.Grounded) { Jump(); } else { _jumpActionEndTime = Time.unscaledTime + _jumpActionTime; } } else if (context.canceled) { StopJumping(); } } Po stlačení tlačidla skoku, ak je postava na zemi, okamžite skočí. V opačnom prípade uložíme časové okno, počas ktorého je možné skok ešte vykonať. Odstránime kontrolu state zo samotnej metódy . Grounded Jump // CharacterController.cs private void Jump() { _isJumping = true; _characterBody.Jump(_jumpSpeed); } Prispôsobíme aj metódu doskoku. Ak bolo tlačidlo uvoľnené pred pristátím, nemalo by dôjsť k skoku, takže resetujeme . _jumpActionEndTime // CharacterController.cs private void StopJumping() { _jumpActionEndTime = 0; //... } Kedy by sme mali skontrolovať, či postava pristála a spustiť skok? Stav sa spracuje v , zatiaľ čo spracovanie akcie nastane neskôr. Bez ohľadu na to, či ide o alebo , medzi pristátím a skokom môže nastať jednosnímkové oneskorenie, čo je citeľné. CharacterBody FixedUpdate Update FixedUpdate Do pridáme udalosť , aby sme okamžite reagovali na pristátie. Prvý argument bude predchádzajúci stav a druhý bude aktuálny stav. CharacterBody StateChanged // CharacterBody.cs public event Action<CharacterState, CharacterState> StateChanged; Upravíme správu stavu tak, aby spustila udalosť zmeny stavu a prepíšeme . FixedUpdate // CharacterBody.cs [field: SerializeField] private CharacterState _state; public CharacterState State { get => _state; private set { if (_state != value) { var previousState = _state; _state = value; StateChanged?.Invoke(previousState, value); } } } Tiež som spresnil, ako sa s pracuje v . surfaceHit FixedUpdate // CharacterBody.cs private void FixedUpdate() { //... if (_velocity.y <= 0 && slideResults.surfaceHit) { var surfaceHit = slideResults.surfaceHit; Velocity = ClipVector(_velocity, surfaceHit.normal); if (surfaceHit.normal.y >= _minGroundVertical) { State = CharacterState.Grounded; return; } } State = CharacterState.Airborne; } V sa prihlásime na odber udalosti a pridáme obslužný program. CharacterController StateChanged // CharacterController.cs private void OnEnable() { _characterBody.StateChanged += OnStateChanged; } private void OnDisable() { _characterBody.StateChanged -= OnStateChanged; } private void OnStateChanged(CharacterState previousState, CharacterState state) { if (state == CharacterState.Grounded) { OnGrounded(); } } Odstránime kontrolu stavu z a presunieme ju do . Grounded Update OnGrounded // CharacterController.cs private void Update() { _characterBody.SetLocomotionVelocity(_locomotionVelocity); } private void OnGrounded() { _isJumping = false; } Teraz pridajte kód a skontrolujte, či sa má spustiť skok. // CharacterController.cs private void OnGrounded() { _isJumping = false; if (_jumpActionEndTime > Time.unscaledTime) { _jumpActionEndTime = 0; Jump(); } } Ak je väčší ako aktuálny čas, znamená to, že tlačidlo skoku bolo stlačené nedávno, takže resetujeme a vykonáme skok. _jumpActionEndTime _jumpActionEndTime Teraz skúste neustále skákať s postavou. Všimnete si, že tlačidlo skoku je citlivejšie a ovládanie postavy je plynulejšie. Všimol som si však, že v určitých situáciách, ako je napríklad roh zobrazený na obrázku nižšie, zaznamená stav mierne oneskorenie, čím sa preruší reťaz skokov. Grounded Aby som to vyriešil, nastavil som pole v komponente na 0,05 namiesto 0,01. Táto hodnota predstavuje minimálnu vzdialenosť od povrchu, aby telo vstúpilo do stavu. Surface Anchor CharacterBody Grounded Cliff Jumping Možno ste si všimli, že pokus o skok pri behu z vertikálnych plôch nie vždy funguje. Niekedy sa môže zdať, že tlačidlo skoku nereaguje. Toto je jedna z jemností vývoja pre 2D plošinovky. Hráči potrebujú schopnosť skákať, aj keď sa stláčaním tlačidla skoku mierne oneskoria. Aj keď sa tento koncept môže zdať čudný, väčšina plošinoviek takto funguje. Výsledkom je postava, ktorá akoby vytláčala vzduch, ako ukazuje animácia nižšie. ovládača znakov Poďme implementovať túto mechaniku. Zavedieme nové pole na uloženie časového okna (v sekundách), počas ktorého môže postava stále skákať po strate stavu . Grounded // CharacterController.cs [Min(0)] [SerializeField] private float _rememberGroundTime = 0.1f; Pridáme tiež ďalšie pole na uloženie časovej pečiatky, po ktorej sa stav „zabudne“. Grounded // CharacterController.cs private float _lostGroundTime; Tento stav sa bude sledovať pomocou udalosti . Na tento účel upravíme handler . CharacterBody OnStateChanged // CharacterController.cs private void OnStateChanged(CharacterState previousState, CharacterState state) { if (state == CharacterState.Grounded) { OnGrounded(); } else if (previousState == CharacterState.Grounded) { _lostGroundTime = Time.unscaledTime + _rememberGroundTime; } } Je dôležité rozlíšiť, či postava stratila stav kvôli úmyselnému skoku alebo z iného dôvodu. Už máme príznak , ktorý sa deaktivuje pri každom volaní , aby sa zabránilo nadbytočným akciám. Grounded _isJumping StopJumping Rozhodol som sa nezaviesť ďalšiu vlajku, pretože zrušenie nadbytočného skoku neovplyvňuje hrateľnosť. Nebojte sa experimentovať. Príznak bude teraz vymazaný len vtedy, keď postava pristane po skoku. Podľa toho aktualizujme kód. _isJumping // CharacterController.cs private void StopJumping() { _jumpActionEndTime = 0; var velocity = _characterBody.Velocity; if (_isJumping && velocity.y > 0) { _characterBody.Velocity = new Vector2( velocity.x, velocity.y / _stopJumpFactor); } } Nakoniec zrevidujeme metódu . OnJump // CharacterController.cs public void OnJump(InputAction.CallbackContext context) { if (context.started) { if (_characterBody.State == CharacterState.Grounded || (!_isJumping && _lostGroundTime > Time.unscaledTime)) { Jump(); } else { _jumpActionEndTime = Time.unscaledTime + _jumpActionTime; } } else if (context.canceled) { StopJumping(); } } Teraz už skákanie z vertikálnych plôch nenarúša herný rytmus a pôsobí oveľa prirodzenejšie, napriek svojej zjavnej absurdnosti. Postava dokáže doslova vytlačiť vzduch a zájde ďalej, než sa zdá logické. Ale to je presne to, čo naša plošinovka potrebuje. Prevrátenie postavy Posledným dotykom je, aby postava stála v smere pohybu. Implementujeme to najjednoduchším spôsobom — zmenou mierky postavy pozdĺž osi x. Nastavenie zápornej hodnoty spôsobí, že náš kapitán bude stáť opačným smerom. Najprv si uložme pôvodnú stupnicu v prípade, že sa líši od 1. // CharacterController.cs public class CharacterController : MonoBehaviour { //... private Vector3 _originalScale; private void Awake() { //... _originalScale = transform.localScale; } } Teraz pri pohybe doľava alebo doprava použijeme kladnú alebo zápornú stupnicu. // CharacterController.cs public class CharacterController : MonoBehaviour { public void OnMove(InputAction.CallbackContext context) { //... // Change character's direction. if (value.x != 0) { var scale = _originalScale; scale.x = value.x > 0 ? _originalScale.x : -_originalScale.x; transform.localScale = scale; } } } Výsledok otestujeme. Zabaliť sa Tento článok sa ukázal byť dosť podrobný, no podarilo sa nám pokryť všetky podstatné aspekty ovládania postavy v 2D plošinovke. Pripomíname, že konečný výsledok si môžete pozrieť vo vetve „ “ v úložisku. Character Controller Ak sa vám páčil alebo vám tento a predchádzajúci článok pomohol, ocenil by som lajky a hviezdičky na GitHub. Neváhajte nás kontaktovať, ak narazíte na nejaké problémy alebo nájdete chyby. Ďakujem za pozornosť!