Übergänge
Teil des Abschnitts Spielarchitektur in Unity der Unity-Journey von Coddy. Lektion 30 von 50.
Wer entscheidet, wann sich der Zustand ändert? Wenn man den Besitzer entscheiden lässt, landen wieder alle Regeln in einem einzigen Update, wie beim switch. Üblicherweise weiß jeder Zustand selbst, wie er verlassen wird: In seinem Tick prüft er seine Bedingung und fragt die Zustandsmaschine nach dem nächsten Zustand. Diese Bombe wartet, bis sich der Spieler nähert, zündet dann ihre Lunte 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 darüber auf die Zustandsmaschine, die Spielerprüfung und die anderen Zustände zu. Die Bombe erstellt jeden Zustand einmal in Start, damit ein Zustand keinen anderen mit new erstellt: Zwei Zustände, die sich in ihren Konstruktoren gegenseitig erstellen, würden so lange in einer Schleife laufen, bis der Stack überläuft. Das Update der Bombe lässt nur die Zustandsmaschine ticken, und die Regeln befinden sich in den Zuständen, zu denen sie gehören: Die 2-Sekunden-Lunte ist ausschließlich 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 Lunte noch einen Frame lang weiterbrennen, obwohl der Zustand bereits beendet ist. Destroy wird als Object.Destroy geschrieben: Ein MonoBehaviour erbt es von Unity's Object-Klasse, und eine einfache Klasse muss die Klasse angeben.
Aufgabe
MittelEine Venusfliegenfalle schnappt nach jedem, der auf sie zugeht. Flytrap.cs ist fertig: Die Zustandsmaschine und die drei Zustände (die öffentlichen Felder open, snap und chew) werden erstellt, der Zustand Open wird gestartet und die Zustandsmaschine wird in jedem Frame aktualisiert. PlayerInReach() gibt an, ob der Spieler am Boden näher als 2 ist. In FlytrapStates.cs sind alle Enter-Methoden bereits geschrieben (die Farben, die Timer, die Chomp!-Meldung). Schreibe die Übergänge in den Tick-Methoden der einzelnen Zustände:
- Open: Wenn der Spieler in Reichweite ist, schnappt die Falle zu.
- Snap dauert 0.5 Sekunden. Danach kaut die Falle, wenn der Spieler noch in Reichweite ist, und öffnet sich wieder, wenn er entkommen ist.
- Chew dauert 3 Sekunden, egal, wer sich in der Nähe befindet. Danach öffnet sich die Falle wieder.
Mit den Pfeiltasten bewegst du den Player. Drücke Play, gehe in die Falle, versuche dann, ihr auszuweichen, und drücke anschließend Check.
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()
{
// Zuschnappen, 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: kauen, wenn der Spieler noch in Reichweite ist, andernfalls wieder öffnen
}
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: wieder öffnen
}
public void Exit()
{
}
}
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