Menu
CoddyTech

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.

challenge icon

Aufgabe

Mittel

Ein 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.cs das Interface public interface IState mit void Enter(), void Tick() und void Exit().
  • Schreibe in TurretStates.cs zwei einfache Klassen, die es implementieren. ScanState erhä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 -45 und 45. 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ßerdem Color.green.
  • AlertState erhält den Kopf und den Spieler. Die Enter-Methode färbt den Kopf Color.red und gibt Intruder! im Log aus; die Tick-Methode dreht den Kopf nur um die y-Achse zum Spieler, ohne ihn zu neigen; die Exit-Methode gibt All clear im Log aus.
  • Erzeuge in Turret.cs beide Zustände in Start und beginne mit dem Scannen. Wechsle in jedem Frame in den Alarmzustand, wenn der Spieler weniger als range (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
quiz iconTeste dich selbst

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