Menu
CoddyTech

Übergänge

Teil des Abschnitts Spielarchitektur in Unity der Unity-Journey von Coddy. Lektion 38 von 64.

Wer entscheidet, wann sich der Zustand ändert? Wenn man den Besitzer fragt, landen alle Regeln wieder in einem einzigen Update, so wie beim switch. Die übliche Antwort lautet, dass jeder Zustand seinen eigenen Ausweg kennt: In seinem Tick prüft er seine Bedingung und bittet die Maschine um den nächsten Zustand. Diese Bombe wartet, bis der Spieler näher kommt, lässt dann ihre Zündschnur abbrennen und kehrt zum Warten zurück, wenn der Spieler wegläuft:

public class Bomb : MonoBehaviour
{
    public Transform player;
    public StateMachine machine = new StateMachine();
    public IState idle;
    public IState fuse;

    void Start()
    {
        idle = new IdleState(this);
        fuse = new FuseState(this);
        machine.ChangeState(idle);
    }

    void Update() { machine.Tick(); }

    public bool PlayerNear()
    {
        return Vector3.Distance(transform.position, player.position) < 3f;
    }
}

public class IdleState : IState
{
    private Bomb bomb;
    public IdleState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { }
    public void Exit() { }

    public void Tick()
    {
        if (bomb.PlayerNear()) bomb.machine.ChangeState(bomb.fuse);
    }
}

public class FuseState : IState
{
    private Bomb bomb;
    private float timer;
    public FuseState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { timer = 2f; }
    public void Exit() { }

    public void Tick()
    {
        if (!bomb.PlayerNear())
        {
            bomb.machine.ChangeState(bomb.idle);
            return;
        }
        timer -= Time.deltaTime;
        if (timer <= 0f)
        {
            Debug.Log("Boom!");
            Object.Destroy(bomb.gameObject);
        }
    }
}

Jeder Zustand erhält die Bombe in seinem Konstruktor und greift über sie auf die Maschine, die Spielerprüfung und die anderen Zustände zu. Die Bombe erstellt jeden Zustand einmal in Start, sodass ein Zustand keinen anderen mit new erstellt: Zwei Zustände, die sich gegenseitig in ihren Konstruktoren erstellen, würden in einer Schleife laufen, bis der Stack überläuft. Das Update der Bombe lässt nur die Maschine ticken, und die Regeln stehen in den Zuständen, zu denen sie gehören: Die 2-Sekunden-Zündschnur ist allein die Sache von FuseState.

ChangeState führt FuseState's Exit und IdleState's Enter sofort aus, aber der Rest von FuseState's Tick würde nach dem Aufruf trotzdem weiterlaufen. Das return stoppt ihn, andernfalls würde die Zündschnur noch einen Frame lang abbrennen, obwohl der Zustand bereits beendet ist. Destroy wird als Object.Destroy geschrieben: Ein MonoBehaviour erbt es von Unitys Object-Klasse, und eine einfache Klasse muss die Klasse angeben.

challenge icon

Aufgabe

Mittel

Schreibe die Übergänge der Fliegenfalle in jedem Tick von FlytrapStates.cs.

  1. Offen: Wenn trap.PlayerInReach() gilt, rufe trap.machine.ChangeState(trap.snap); auf.
  2. Zuschnappen: Addiere Time.deltaTime zu timer; wechsle bei 0.5 zu trap.chew, wenn der Spieler noch in Reichweite ist, andernfalls zu trap.open.
  3. Kauen: Zähle timer von 3 herunter; wechsle bei 0 zu trap.open.

Drücke „Spielen“, laufe in die Falle und weiche ihr dann aus. Drücke danach „Prüfen“.

Probier es selbst

using UnityEngine;

public class OpenState : IState
{
    private Flytrap trap;

    public OpenState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        trap.Paint(new Color(0.3f, 0.8f, 0.2f));
    }

    public void Tick()
    {
        // Schnappe zu, wenn der Spieler in Reichweite ist
    }

    public void Exit()
    {
    }
}

public class SnapState : IState
{
    private Flytrap trap;
    private float timer;

    public SnapState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 0f;
        trap.Paint(new Color(1f, 0.55f, 0.1f));
    }

    public void Tick()
    {
        // Nach 0.5 Sekunden: Kaue, wenn der Spieler noch in Reichweite ist, andernfalls öffne dich wieder
    }

    public void Exit()
    {
    }
}

public class ChewState : IState
{
    private Flytrap trap;
    private float timer;

    public ChewState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 3f;
        trap.Paint(new Color(0.5f, 0.05f, 0.1f));
        Debug.Log("Chomp!");
    }

    public void Tick()
    {
        // Nach 3 Sekunden: Öffne dich wieder
    }

    public void Exit()
    {
    }
}

Alle Lektionen in Spielarchitektur in Unity

Übe selbstständig: Unity-Playground