Zustände als Klassen
Teil des Abschnitts Spielarchitektur in Unity der Unity-Journey von Coddy. Lektion 28 von 50.
Der Switch über ein Enum aus Abschnitt 3 hält die Arbeit aller Zustände in einem Update. Das lässt sich nicht mehr gut erweitern, sobald Zustände eigene Daten haben: Ein Timer, den nur ein Zustand verwendet, wird zu einem Feld des MonoBehaviour, neben den Feldern aller anderen Zustände, und jeder Case muss daran denken, die verwendeten Werte zurückzusetzen. Das Zustandsmuster aus C# gibt jedem Zustand eine eigene Klasse, und jede Klasse implementiert eine Schnittstelle. Diese Straßenbake blinkt oder leuchtet dauerhaft, und mit der Leertaste wechselst du zwischen den beiden Zuständen:
public interface IState
{
void Enter();
void Tick();
void Exit();
}
public class BlinkState : IState
{
private Renderer lamp;
private float timer;
public BlinkState(Renderer lamp)
{
this.lamp = lamp;
}
public void Enter() { timer = 0f; }
public void Tick()
{
timer += Time.deltaTime;
if (timer >= 0.5f)
{
timer = 0f;
lamp.enabled = !lamp.enabled;
}
}
public void Exit() { lamp.enabled = true; }
}
public class SteadyState : IState
{
public void Enter() { }
public void Tick() { }
public void Exit() { }
}
public class Beacon : MonoBehaviour
{
public Renderer lamp;
private IState blink;
private IState steady;
private IState current;
void Start()
{
blink = new BlinkState(lamp);
steady = new SteadyState();
SetState(steady);
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame) SetState(current == steady ? blink : steady);
current.Tick();
}
void SetState(IState next)
{
if (current != null) current.Exit();
current = next;
current.Enter();
}
}Die Zustände sind einfache C#-Klassen, keine MonoBehaviours. Du erstellst sie mit new und übergibst ihnen über den Konstruktor, was sie benötigen – hier den Renderer der Lampe –, da sie keine Komponenten sind und weder GetComponent aufrufen noch Felder im Inspector anzeigen können. Unity ruft ihre Methoden auch nie auf: Beacon ist das MonoBehaviour, daher ruft sein Update in jedem Frame current.Tick() auf.
Der timer gehört ausschließlich zu BlinkState, und dessen Enter setzt ihn jedes Mal auf 0, wenn die Bake zu blinken beginnt. Exit sorgt beim Beenden des Zustands für Ordnung: Die Leertaste kann gedrückt werden, während die Lampe ausgeschaltet ist, und Exit schaltet sie wieder ein, sodass SteadyState nie mit einer dunklen Lampe beginnt. SetState führt Exit des alten Zustands und dann Enter des neuen Zustands aus. Ein dritter Zustand, etwa ein langsames Pulsieren, ist eine weitere Klasse, und Beacons Update bleibt unverändert.
Aufgabe
MittelEin Wachgeschütz bewacht den Hof. Mit den Pfeiltasten bewegst du den Player (bereits vorbereitet). Gib dem Geschütz zwei Zustände, die als Klassen implementiert sind:
- Deklariere in
IState.csdas Interfacepublic interface IStatemitvoid Enter(),void Tick()undvoid Exit(). - Schreibe in
TurretStates.cszwei einfache Klassen, die es implementieren.ScanStateerhält den Kopf des Geschützes im Konstruktor. Der Zustand schwenkt den Kopf hin und her: Der lokale y-Winkel des Kopfes bewegt sich mit 90 Grad pro Sekunde zwischen-45und45. Er beginnt bei 0 und bewegt sich zu Beginn jedes Scanvorgangs zunächst in die positive Richtung. Die Enter-Methode färbt den Kopf außerdemColor.green. AlertStateerhält den Kopf und den Spieler. Die Enter-Methode färbt den KopfColor.redund gibtIntruder!im Log aus; die Tick-Methode dreht den Kopf nur um die y-Achse zum Spieler, ohne ihn zu neigen; die Exit-Methode gibtAll clearim Log aus.- Erzeuge in
Turret.csbeide Zustände in Start und beginne mit dem Scannen. Wechsle in jedem Frame in den Alarmzustand, wenn der Spieler weniger alsrange(4) vom Geschütz entfernt ist, und zurück zum Scannen, wenn das nicht der Fall ist. Führe danach Tick für den aktuellen Zustand aus. Bei einem Wechsel wird zuerst Exit für den alten Zustand und dann Enter für den neuen Zustand ausgeführt.
Drücke auf Play, gehe zum Geschütz und wieder von ihm weg und klicke dann auf Check.
Probier es selbst
using UnityEngine;
// ScanState und AlertState
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Spielarchitektur in Unity
1Komponenten statt Hierarchien
Kleine KomponentenEin Basisskript für GegnerAbstrakte KomponentenKomponenten im ZusammenspielRückblick: Modularer Geschützturm4Serialisierte Daten
Serialisierbare KlassenListen im InspectorEnums im InspectorRückblick: Loot-Tabelle7Zustandsautomaten
Zustände als KlassenEin ZustandsautomatÜbergängeGegner-KIWiederholung: Türzustände10Projekt: Arena-Überlebenskampf
Der HeldGegnertypen2Interfaces in Unity
IDamageableInterfaces und TryGetComponentGetComponentsRückblick: Interaktionstaste5ScriptableObjects
Daten-AssetsWaffenwerteGemeinsame EinstellungenWellen als DatenRückblick: Item-Datenbank8Wiederverwendbarer Code
ErweiterungsmethodenEin generisches SingletonGenerische HilfsmethodenRückblick: FindNearest