Unity3D Design Patterns – State – Building a Unity3D State Machine

  • Domů /
  • Unity3D
  • / Unity3D Design Patterns – State – Building a Unity3D State Machine
By Jason Weimann /May 26, 2017

Návrhové vzory jsou opakovaně použitelná řešení běžných problémů vyskytujících se v softwarových projektech. Používání dobrých návrhových vzorů v Unity může pomoci udržet váš projekt čistý, udržovatelný a snadno rozšiřitelný. Usnadňuje také jejich sestavování a.. Dnes se budu zabývat stavovým vzorem a základy sestavení stavového stroje v Unity3D.

Stavový vzor je návrhový vzor behaviorálního softwaru, který implementuje stavový stroj objektově orientovaným způsobem. … Tento vzor se používá v počítačovém programování k zapouzdření různého chování stejného objektu na základě jeho vnitřního stavu.

Implementace, která se mi nelíbí.

Existuje několik způsobů, jak lze v Unity implementovat stavový vzor. Jeden z běžných způsobů, který vídám, se provádí pomocí příkazu switch. Obvykle to vypadá nějak takto.

Problémy

Ačkoli to technicky funguje, je tu několik vážných problémů, které časem vyvstanou.

Předně, veškerá naše logika je nyní uvnitř této jediné třídy. Útok, obrana, návrat domů a všechny další stavy, které chceme vytvořit, musí být přidány sem. To povede k velkému nafouknutí hlavní třídy pro naši umělou inteligenci. Také to zvýší pravděpodobnost, že se náš kód rozbije, protože pro každý typ logiky upravujeme stejnou třídu. Zanedlouho by se tato třída mohla snadno proměnit ve více než tisíciřádkovou katastrofu.

Neexistuje také skvělý způsob, jak se vypořádat s událostmi, které nastanou při vstupu a výstupu ze stavu. Poměrně často chceme, aby postava nebo objekt něco udělal, když vstoupí do stavu, zopakoval akci, když je v tomto stavu, a udělal něco úplně jiného, když stav opustí.

Při tomto nastavení bychom museli přidat další přepínače pro vstup a výstup ze stavu, což by třídu dále nafouklo a vše by bylo hůře pochopitelné.

Lepší implementace

Lepším přístupem je, aby každý stav byl vlastní třídou, zpravidla se základní třídou „stav“, od které dědí.

Takto stačí, aby postava nebo objekt měly odkaz na svůj aktuální stav a volání, které provede aktualizaci tohoto stavu. Když chceme přidat nový stav, prostě vytvoříme novou třídu.

Při použití této metody vypadá naše třída postavy trochu jinak:

Můžete si všimnout, že třídy jsou přibližně stejně velké… ale to jen proto, že první verze vlastně nic nedělá (hlavně aby to bylo stravitelné). Kdyby ta první třída dělala všechno, co dělá systém, který vám ukážu, byla by obrovská.

Jak to funguje?

Podíváte-li se na metodu Update, zjistíte, že jednoduše volá metodu Tick() na aktuální stav. Tímto voláním stav řídí, co bude postava dělat každý snímek.

Máme také metodu SetState, která nám umožňuje předat stav, do kterého chceme, aby postava vstoupila. Voláním metody SetState a předáváním různých stavů můžeme zcela změnit chování této postavy, aniž bychom měnili cokoli v samotné třídě postavy.

V závislosti na vašem nastavení může být vhodné udržovat stavy a přepínat mezi nimi, abychom se vyhnuli garbage collection. Aby to bylo srozumitelné, přeskočil jsem to, ale stojí za to to mít na paměti, pokud jsou změny stavů rychlé a jste na platformě, kde na tom záleží.

Co je to třída State?“

Třída State je abstraktní základní třída, kterou budeme používat pro všechny vytvářené stavy. Definuje velmi základní kontrakt pro to, co musí stav implementovat, a další věci, které může volitelně implementovat.

Do této základní třídy stavů je také přidán odkaz na znak, protože všechny naše stavy v tomto příkladu budou nějakým způsobem spolupracovat se znakem. Znak převezmeme v konstruktoru naší třídy stavů a uchováme ho v chráněné proměnné, aby byl k dispozici našim třídám stavů.

Máme také abstraktní metodu Tick(). To, že ji uděláme abstraktní, znamená, že naše stavové třídy jsou nuceny ji implementovat.

Naopak máme dvě virtuální metody OnStateEnter() & OnStateExit(), které jsou volitelné. Protože jsou virtuální, mají výchozí implementaci, která nic nedělá, a není nutné je implementovat.

To je celá třída stavů… docela jednoduché, že?

Vytvoření stavu – Návrat domů

Teď, když máme základy, vytvoříme implementaci stavu, který můžeme připojit k naší postavě.

Stav Návrat domů jednoduše způsobí, že se postava vrátí na své domovské místo. Protože ve skutečnosti nedefinujeme cíl pro home, je to výchozí vektor3.zero.

Tick

Metoda Tick říká postavě, aby se přesunula k cíli (0, 0, 0).

Metoda MoveToward je na verzi postavy na konci tohoto příspěvku. Ve skutečném projektu by tento pohyb pravděpodobně řídila jiná komponenta, například třída ‚CharacterMovement‘ nebo něco jiného.

Poté zkontroluje, zda se postava dostala domů, a pokud ano, řekne postavě, aby se přepnula do stavu ‚putování‘. Všimněte si, že při vytváření nového stavu předáváme postavu. Je to proto, že náš stav je navržen tak, aby vyžadoval znak.

EnterState

Při vstupu do stavu návratu domů prostřednictvím OnStateEnter také změníme barvu znaku. Takže když se postava vrací domů, je modrá.

Vytvoření dalšího stavu – Wander

Aby měl stavový stroj smysl, potřebujeme více než jeden stav. Ve skutečnosti chce náš stav návratu domů vlastně přepnout postavu do stavu putování, jakmile skončí, takže potřebujeme vytvořit tento stav putování.

Stav putování vybere náhodné místo a posune postavu směrem k této pozici. Jakmile této pozice dosáhne, vybere další místo, ke kterému bude putovat.

Takto bude pokračovat, dokud nebude putovat 5 sekund (podle naší proměnné wanderTime). V tu chvíli řekne postavě, aby se vrátila do stavu ReturnHome (řádek 43).

Stav putování také změní barvu naší postavy na zelenou prostřednictvím metody OnStateEnter.

Podívejme se, jak to vypadá

Tady jsem vytvořil scénu s tuctem „postav“ reprezentovaných jako kostky.

Uvidíte, jak se potulují, dokud jsou zelené, pak se zase vrátí domů, když jsou modré, a proces se opakuje.

Plná postava

Výše jsem zmínil, že je zde plná třída postavy s metodou MoveToward. V reálném projektu bych opět doporučoval mít základní pohyb oddělený v jiné komponentě, ale aby to bylo jednoduché, je zde ve třídě Character.

Závěry

Stavový vzor je úžasně mocný. Jakmile se s tímto vzorem sžijete, můžete snadno vytvářet systémy umělé inteligence, menu a další.

Tento stavový stroj je také velmi základní a lze jej rozšířit o věci, jako je ukládání stavů do mezipaměti (abyste se vyhnuli nové alokaci paměti), nebo jej více přizpůsobit vašim potřebám.

Další skvělý způsob vytváření stavových strojů je pomocí skriptovatelných objektů. Stavový stroj nastavený pomocí scriptableobjects mohou návrháři snadno konfigurovat v editoru/inspectoru, aniž by se dotkli kódu, ale to je trochu složitější téma, do kterého se možná ponořím později.

Příště, až budete potřebovat vytvořit trochu herní logiky, zkuste si nastavit vlastní stavový stroj (nebo zkopírujte tento jako výchozí bod), určitě vám to pomůže a nový vzor se vám bude líbit.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.