Finite State Machine in Unity
So setzt du eine Finite State Machine in Unity um

Eine Finite State Machine (FSM), auf Deutsch endlicher Automat, ist ein Berechnungsmodell für Algorithmen und Systeme, die sich zu jedem Zeitpunkt in genau einem von endlich vielen Zuständen befinden. In der Spieleentwicklung werden FSMs häufig genutzt, um das Verhalten von Spielobjekten zu steuern, etwa von Figuren, Gegnern oder Spielmechaniken. Eine FSM besteht aus einer Menge von Zuständen, den Übergängen zwischen ihnen und Aktionen, die beim Betreten, beim Verlassen oder während eines Zustands ausgeführt werden.
Die Klasse FiniteStateMachine
Zuerst brauchen wir eine Basisklasse für unsere Finite State Machine. Diese Klasse verwaltet den aktuellen Zustand und kümmert sich um die Zustandsübergänge. Sie hat eine Methode GetInitialState, die überschrieben werden kann, um den Startzustand der FSM festzulegen. Außerdem enthält sie Methoden, die den aktuellen Zustand während der Unity-Lifecycle-Methoden (Update, FixedUpdate und LateUpdate) aktualisieren. Und es gibt eine Methode ChangeState, die Übergänge zwischen Zuständen abwickelt.
using UnityEngine;
public class FiniteStateMachine : MonoBehaviour {
[SerializeField] protected FiniteStateMachineState state;
private void Start() {
state = GetInitialState();
state?.Enter();
}
private void Update() {
state?.Update();
}
private void FixedUpdate() {
state?.FixedUpdate();
}
private void LateUpdate() {
state?.LateUpdate();
}
public void ChangeState(FiniteStateMachineState newState) {
state?.Exit();
state = newState;
state?.Enter();
}
protected virtual FiniteStateMachineState GetInitialState() {
return null;
}
}Die Klasse FiniteStateMachineState
Als Nächstes brauchen wir eine Basisklasse für die Zustände unserer FSM. Diese Klasse definiert die Methoden, die einzelne Zustände überschreiben können, um ihr Verhalten umzusetzen. Jeder Zustand hat eine Referenz auf die FSM, zu der er gehört, sodass er bei Bedarf selbst Zustandsübergänge auslösen kann.
using System;
[Serializable]
public class FiniteStateMachineState {
public string name;
protected FiniteStateMachine fsm;
public FiniteStateMachineState(string name, FiniteStateMachine fsm) {
this.name = name;
this.fsm = fsm;
}
public virtual void Enter() { }
public virtual void Update() { }
public virtual void FixedUpdate() { }
public virtual void LateUpdate() { }
public virtual void Exit() { }
}Mit diesen beiden Klassen kannst du konkrete Zustände erstellen, indem du von FiniteStateMachineState erbst und das gewünschte Verhalten in den überschriebenen Methoden umsetzt.
Anwendungsbeispiel
Hier ist ein Beispiel dafür, wie du mit dem FSM-Pattern eine einfache Entity mit zwei Zuständen erstellst: IdleState und MoveState. Nichts Besonderes, einfach zwei Sekunden im Idle-Zustand bleiben, dann in den Move-Zustand wechseln und zu einer zufälligen Position laufen, dann zurück in den Idle-Zustand und so weiter.
using UnityEngine;
public class Entity : FiniteStateMachine {
public float moveSpeed = 5f;
public Vector3 targetPosition;
public EntityMoveState moveState;
public EntityIdleState idleState;
void Awake() {
moveState = new EntityMoveState(this);
idleState = new EntityIdleState(this);
}
protected override FiniteStateMachineState GetInitialState() {
return idleState;
}
}using UnityEngine;
public class EntityIdleState : FiniteStateMachineState {
Entity entity;
float maxIdleTime = 2f;
float idleStartTime;
public EntityIdleState(Entity _entity) : base("Idle", _entity) {
entity = _entity;
}
public override void Enter() {
base.Enter();
idleStartTime = Time.time;
}
public override void Update() {
base.Update();
// After idling for a certain time, switch to move state
if (Time.time - idleStartTime >= maxIdleTime) {
fsm.ChangeState(entity.moveState);
}
}
}using UnityEngine;
public class EntityMoveState : FiniteStateMachineState {
Entity entity;
public EntityMoveState(Entity _entity) : base("Move", _entity) {
entity = _entity;
}
public override void Enter() {
base.Enter();
// Set a random target position within a certain range
entity.targetPosition = new Vector3(Random.Range(-10f, 10f), 0, Random.Range(-10f, 10f));
}
public override void Update() {
base.Update();
Vector3 direction = (entity.targetPosition - entity.transform.position).normalized;
entity.transform.position += direction * entity.moveSpeed * Time.deltaTime;
if (Vector3.Distance(entity.transform.position, entity.targetPosition) < 0.1f) {
// Reached target position, switch to idle state
fsm.ChangeState(entity.idleState);
}
}
}Das ist die Grundlage für viel komplexere Finite State Machines, die du in Unity bauen kannst.