Unity3D Design Patterns – State – Building a Unity3D State Machine

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

Designmuster sind wiederverwendbare Lösungen für häufige Probleme in Softwareprojekten. Die Verwendung guter Design Patterns in Unity kann dazu beitragen, dass Ihr Projekt sauber, wartbar und einfach zu erweitern ist. Es macht sie auch einfacher zu bauen… Heute werde ich das State-Pattern und die Grundlagen des Aufbaus einer State-Machine in Unity3D behandeln.

Das State-Pattern ist ein verhaltensorientiertes Software-Design-Pattern, das eine State-Machine auf objektorientierte Weise implementiert. … Dieses Muster wird in der Computerprogrammierung verwendet, um unterschiedliches Verhalten für dasselbe Objekt auf der Grundlage seines internen Zustands zu kapseln.

Eine Implementierung, die ich nicht mag.

Es gibt einige Möglichkeiten, wie man ein Zustandsmuster in Unity implementieren kann. Eine der häufigsten Methoden, die ich sehe, ist die Verwendung der switch-Anweisung. Es sieht im Allgemeinen so aus.

Probleme

Während dies technisch funktioniert, gibt es einige ernsthafte Probleme, die im Laufe der Zeit auftauchen werden.

Erstens befindet sich unsere gesamte Logik nun innerhalb dieser einen Klasse. Angreifen, verteidigen, nach Hause zurückkehren und alle anderen Zustände, die wir erzeugen wollen, müssen alle hier hinzugefügt werden. Dies wird zu einer großen, aufgeblähten Master-Klasse für unsere KI führen. Außerdem wird unser Code dadurch anfälliger für Fehler, da wir für jede Art von Logik dieselbe Klasse bearbeiten müssen. Schon bald könnte sich diese Klasse in ein 1000-Zeilen-Desaster verwandeln.

Es gibt auch keine gute Möglichkeit, mit Ereignissen umzugehen, die beim Eintritt in den Zustand und beim Verlassen des Zustands auftreten. Oft wollen wir, dass ein Charakter oder ein Objekt etwas tut, wenn es in einen Zustand eintritt, eine Aktion wiederholt, während es in diesem Zustand ist, und etwas völlig anderes tut, wenn es den Zustand verlässt.

Mit diesem Aufbau müssten wir mehr Schalter für das Betreten und Verlassen des Zustands hinzufügen, was die Klasse weiter aufblähen und alles schwieriger zu verstehen machen würde.

Eine bessere Implementierung

Ein besserer Ansatz besteht darin, dass jeder Zustand eine eigene Klasse ist, im Allgemeinen mit einer Basisklasse „Zustand“, von der sie erben.

Auf diese Weise muss die Figur oder das Objekt nur eine Referenz auf den aktuellen Zustand haben und einen Aufruf, um diesen Zustand zu aktualisieren. Wenn wir einen neuen Zustand hinzufügen wollen, erstellen wir einfach eine neue Klasse.

Mit dieser Methode sieht unsere Charakterklasse etwas anders aus:

Sie werden vielleicht bemerken, dass die Klassen ungefähr gleich groß sind… aber das liegt nur daran, dass die erste Version eigentlich gar nichts tut (hauptsächlich, um sie verdaulich zu halten). Wenn diese erste Klasse alles tun würde, was das System, das ich Ihnen zeigen werde, tut, wäre sie riesig.

Wie funktioniert es?

Wenn Sie sich die Update-Methode ansehen, werden Sie sehen, dass sie einfach die Tick()-Methode für den aktuellen Zustand aufruft. Mit diesem Aufruf steuert der Zustand, was die Figur in jedem Frame tun wird.

Wir haben auch eine SetState-Methode, mit der wir einen Zustand übergeben können, in den die Figur eintreten soll. Indem wir SetState aufrufen und verschiedene Zustände übergeben, können wir das Verhalten dieser Figur komplett ändern, ohne irgendetwas in der Figurenklasse selbst zu ändern.

Abhängig von Ihrem Setup kann es ratsam sein, Zustände zu behalten und zwischen ihnen zu wechseln, um Garbage Collection zu vermeiden. Um dies einfach zu verstehen, habe ich das übersprungen, aber es lohnt sich, daran zu denken, wenn Ihre Zustandsänderungen schnell sind und Sie auf einer Plattform arbeiten, wo es wichtig ist.

Was ist die State-Klasse?

Die State-Klasse ist eine abstrakte Basisklasse, die wir für alle Zustände verwenden, die wir erstellen. Sie definiert einen sehr grundlegenden Vertrag für das, was ein Zustand implementieren muss, und andere Dinge, die er optional implementieren kann.

Die Zeichenreferenz wird ebenfalls zu dieser State-Basisklasse hinzugefügt, da alle unsere Zustände in diesem Beispiel in irgendeiner Weise eine Schnittstelle zu einem Zeichen haben werden. Wir nehmen das Zeichen im Konstruktor unserer Zustandsklasse und speichern es in einer geschützten Variablen, damit es unseren Zustandsklassen zur Verfügung steht.

Wir haben auch die abstrakte Methode Tick(). Sie abstrakt zu machen bedeutet, dass unsere Zustandsklassen gezwungen sind, sie zu implementieren.

Im Gegensatz dazu haben wir zwei virtuelle Methoden für OnStateEnter() & OnStateExit(), die optional sind. Weil sie virtuell sind, haben sie eine Standardimplementierung, die nichts tut, und müssen nicht implementiert werden.

Das ist die Gesamtheit der Zustandsklasse… ziemlich einfach, oder?

Erstellen eines Zustands – Heimkehr

Nun, da wir die Grundlagen haben, lasst uns eine Implementierung eines Zustands erstellen, die wir mit unserem Charakter verbinden können.

Der Heimkehrzustand lässt den Charakter einfach zu seinem Heimatort zurückkehren. Da wir das Ziel für home nicht wirklich definieren, ist es der Standard Vector3.zero.

Tick

Die Tick-Methode weist die Figur an, sich in Richtung des Ziels (0, 0, 0) zu bewegen.

Die MoveToward-Methode befindet sich in einer Version der Figur am Ende dieses Beitrags. In einem realen Projekt würde diese Bewegung wahrscheinlich von einer anderen Komponente wie einer ‚CharacterMovement‘-Klasse oder etwas anderem gesteuert werden.

Dann wird überprüft, ob die Figur es nach Hause geschafft hat, und wenn ja, wird die Figur angewiesen, in einen ‚Wander‘-Zustand zu wechseln. Beachten Sie, dass wir bei der Erstellung des neuen Zustands die Spielfigur mitgeben.

EnterState

Wir ändern auch die Farbe der Figur, wenn der Heimkehrzustand über OnStateEnter betreten wird. Wenn eine Figur also nach Hause geht, ist sie blau.

Einen weiteren Zustand erzeugen – Wander

Für einen Zustandsautomaten brauchen wir mehr als einen Zustand. Tatsächlich will unser Heimkehr-Zustand die Figur in einen Wander-Zustand versetzen, sobald er fertig ist, also müssen wir diesen Wander-Zustand erstellen.

Der Wander-Zustand wählt eine zufällige Position und bewegt die Figur zu dieser Position. Wenn sie diese Position erreicht hat, sucht sie sich einen anderen Ort aus, zu dem sie wandern kann.

Dies geschieht so lange, bis sie 5 Sekunden lang gewandert ist (basierend auf unserer Variable wanderTime). An diesem Punkt wird der Charakter angewiesen, in den ReturnHome-Zustand zurückzukehren (Zeile 43).

Der Wanderzustand ändert auch die Farbe unseres Charakters zu grün über die OnStateEnter-Methode.

Lassen Sie uns sehen, wie es aussieht

Hier habe ich eine Szene mit einem Dutzend der „Charaktere“ als Würfel dargestellt.

Sie werden sehen, wie sie herumwandern, wenn sie grün sind, dann wieder nach Hause gehen, wenn sie blau sind, und den Prozess wiederholen.

Der vollständige Charakter

Ich habe oben erwähnt, dass die vollständige Charakterklasse hier mit der MoveToward-Methode ist. Auch hier würde ich in einem echten Projekt empfehlen, die grundlegende Bewegung in einer anderen Komponente aufzubewahren, aber um dies einfach zu halten, ist sie hier in der Charakterklasse.

Schlussfolgerungen

Das Zustandsmuster ist erstaunlich mächtig. Sie können KI-Systeme, Menüs und vieles mehr mit Leichtigkeit erstellen, sobald Sie sich mit diesem Muster vertraut gemacht haben.

Dieser Zustandsautomat ist auch sehr grundlegend und kann erweitert werden, um Dinge wie das Zwischenspeichern von Zuständen zu tun (um neue Speicherzuweisungen zu vermeiden) oder um mehr an Ihre Bedürfnisse angepasst zu werden.

Eine weitere großartige Möglichkeit, Zustandsautomaten zu erstellen, sind skriptfähige Objekte. Ein Zustandsautomat, der scriptableobjects verwendet, kann von Designern leicht im Editor/Inspector konfiguriert werden, ohne dass sie jemals den Code berühren müssen, aber das ist ein etwas komplizierteres Thema, auf das ich vielleicht später eingehen werde.

Nächstes Mal, wenn du ein wenig Spiellogik bauen musst, versuche einen eigenen Zustandsautomaten zu erstellen (oder kopiere diesen als Ausgangspunkt), ich bin sicher, es wird dir helfen und du wirst das neue Muster genießen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.