Beim Programmieren von In-Game-Entitäten treten Situationen auf, in denen sie unter unterschiedlichen Bedingungen auf unterschiedliche Weise agieren müssen, was auf die Verwendung von
Zuständen hindeutet.
Wenn Sie sich jedoch für Brute Force entscheiden, verwandelt sich der Code schnell in ein Chaos mit vielen verschachtelten if-else-Anweisungen.
Für eine ordnungsgemäße Lösung dieses Problems können Sie das Entwurfsmuster "Status" verwenden. Wir werden ihm dieses Tutorial widmen!
Aus dem Tutorial Sie:
- Lernen Sie die Grundlagen der Statusvorlage in Unity kennen.
- Sie erfahren, was eine Zustandsmaschine ist und wann sie verwendet werden muss.
- Erfahren Sie, wie Sie diese Konzepte verwenden, um die Bewegung Ihres Charakters zu steuern.
Hinweis : Dieses Tutorial richtet sich an fortgeschrittene Benutzer. Es wird davon ausgegangen, dass Sie bereits mit Unity vertraut sind und über durchschnittliche Kenntnisse in C # verfügen. Darüber hinaus verwendet dieses Lernprogramm Unity 2019.2 und C # 7.
An die Arbeit gehen
Projektmaterialien herunterladen. Entpacken Sie die
Zip-Datei und öffnen Sie das Startprojekt in Unity.
Das Projekt enthält mehrere Ordner, die Ihnen den Einstieg erleichtern. Der Ordner
Assets / RW enthält die Ordner
Animationen ,
Materialien ,
Modelle ,
Prefabs ,
Ressourcen ,
Szenen ,
Skripte und
Sounds , die nach den darin enthaltenen Ressourcen benannt sind.
Um das Tutorial zu vervollständigen, arbeiten wir nur mit
Szenen und
Skripten .
Gehen Sie zu
RW / Scenes und öffnen Sie
Main . Im Spielmodus sehen Sie eine Figur in einer Kapuze in einer mittelalterlichen Burg.
Klicken Sie auf "
Abspielen" und beobachten Sie, wie sich die
Kamera an den
Zeichenrahmen anpasst. Momentan gibt es in unserem kleinen Spiel keine Interaktionen, wir werden sie im Tutorial bearbeiten.
Erkunde den Charakter
Wählen Sie in der
Hierarchie Zeichen . Überprüfen Sie den
Inspector . Sie sehen eine
Komponente mit demselben Namen, die die Steuerlogik für
Zeichen enthält.
Öffnen Sie
Character.cs in
RW / Scripts .
Das Skript führt viele Aktionen aus, die meisten sind jedoch für uns nicht wichtig. Beachten wir zunächst die folgenden Methoden.
Move : Bewegt den Charakter und empfängt Werte vom Typ Gleitgeschwindigkeit als Bewegungsgeschwindigkeit und rotationSpeed als Winkelgeschwindigkeit.ResetMoveParams : Diese Methode setzt die Parameter zurück, mit denen die Bewegung und die Winkelgeschwindigkeit des Zeichens animiert werden . Es wird nur zur Reinigung verwendet.SetAnimationBool : param Parameter param animation vom Typ Bool auf value.CheckCollisionOverlap : Er empfängt einen Typ Vector3 und gibt ein Vector3 zurück, das bestimmt, ob sich innerhalb des angegebenen Radius vom Vector3 befinden.TriggerAnimation : TriggerAnimation den Animationsparameter des Eingabeparameters param .ApplyImpulse : ApplyImpulse Impuls auf Character an, der dem Eingabeparameter force Typ Vector3 .
Nachfolgend sehen Sie diese Methoden. In unserem Tutorial sind deren Inhalt und interne Arbeit nicht wichtig.
Was sind Zustandsautomaten?
Eine Zustandsmaschine ist ein Konzept, bei dem ein Container den Zustand von etwas zu einem bestimmten Zeitpunkt speichert. Basierend auf den Eingabedaten kann es abhängig vom aktuellen Status eine Schlussfolgerung geben, die in diesem Prozess in einen neuen Status übergeht. Zustandsautomaten können als
Zustandsdiagramm dargestellt werden . Wenn Sie ein Zustandsdiagramm erstellen, können Sie alle möglichen Zustände des Systems und die Übergänge zwischen ihnen durchdenken.
Zustandsautomaten
Finite State Machines oder
FSM (Finite State Machine) ist eine der vier Hauptfamilien von
Maschinen . Automaten sind abstrakte Modelle einfacher Maschinen. Sie werden im Rahmen der
Automatentheorie - dem theoretischen Zweig der Informatik - untersucht.
Kurzgesagt:
- FSM besteht aus einer endlichen Menge von Bedingungen . Zu jedem Zeitpunkt ist nur einer dieser Zustände aktiv .
- Jeder Zustand bestimmt, in welchen Zustand er als Ausgabe geht, basierend auf der empfangenen Sequenz eingehender Informationen .
- Der Ausgangszustand wird zum neuen aktiven Zustand. Mit anderen Worten, es gibt einen Übergang zwischen Zuständen .
Um dies besser zu verstehen, betrachten Sie den Charakter eines Plattformspiels, das vor Ort ist. Der Charakter befindet sich im
stehenden Zustand. Dies ist sein
aktiver Zustand, bis der Spieler den Knopf drückt, so dass der Charakter springt.
Der Status "
Stehend" kennzeichnet einen Tastendruck als signifikanten
Eingang und wechselt als
Ausgang in den Status "
Springen" .
Angenommen, es gibt eine bestimmte Anzahl solcher Bewegungszustände, und ein Charakter kann sich jeweils nur in einem der Zustände befinden. Dies ist ein Beispiel für FSM.
Hierarchische Zustandsmaschinen
Stellen Sie sich einen Plattformer mit FSM vor, in dem mehrere Zustände eine gemeinsame Physiklogik aufweisen. Sie können sich beispielsweise in den Zuständen
Hocken und
Stehen bewegen und springen. In diesem Fall führen mehrere eingehende Variablen für zwei verschiedene Zustände zur gleichen Verhaltens- und Informationsausgabe.
In einer solchen Situation wäre es logisch, das allgemeine Verhalten an einen anderen Staat zu delegieren. Glücklicherweise kann dies mit
hierarchischen Zustandsautomaten erreicht werden.
In einem hierarchischen FSM gibt es
Unterzustände, die eingehende
Rohdaten an ihre
Unterzustände delegieren . Auf diese Weise können Sie die Größe und Komplexität des FSM unter Beibehaltung seiner Logik elegant reduzieren.
Statusvorlage
In ihrem Buch
Design Patterns: Elemente wiederverwendbarer objektorientierter Software definierten Erich Gamma, Richard Helm, Ralph Johnson und John Vlissidis (
Die Viererbande ) die
Aufgabe der Vorlage State wie folgt:
„Er muss dem Objekt erlauben, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert. In diesem Fall hat das Objekt anscheinend seine Klasse geändert. “
Um dies besser zu verstehen, betrachten Sie das folgende Beispiel:
- Ein Skript, das eingehende Informationen für die Bewegungslogik empfängt, ist an eine Entität im Spiel angehängt.
- Diese Klasse speichert eine aktuelle Statusvariable, die sich einfach auf eine Instanz der Statusklasse bezieht.
- Eingehende Informationen werden an diesen aktuellen Status delegiert, der sie verarbeitet und ein in sich selbst definiertes Verhalten erzeugt. Es behandelt auch die erforderlichen Zustandsübergänge.
Aufgrund der Tatsache, dass sich die
aktuelle Statusvariable zu unterschiedlichen Zeitpunkten auf unterschiedliche
Status bezieht, scheint es, dass sich dieselbe Skriptklasse unterschiedlich verhält. Dies ist das Wesentliche der Vorlage "Status".
In unserem Projekt verhält sich die oben genannte
Zeichenklasse je nach Status unterschiedlich. Aber wir brauchen ihn, um sich zu benehmen!
Im Allgemeinen gibt es drei Schlüsselpunkte für jede Zustandsklasse, die das Verhalten des gesamten Staates ermöglichen:
- Eintrag : Dies ist der Moment, in dem eine Entität in einen Zustand eintritt und Aktionen ausführt, die beim Eintritt in den Zustand nur einmal ausgeführt werden müssen.
- Beenden : Ähnlich wie bei der Eingabe werden hier alle Rücksetzvorgänge ausgeführt, die nur ausgeführt werden müssen, bevor sich der Status ändert.
- Update-Schleife : Hier ist die grundlegende Update-Logik , die in jedem Frame ausgeführt wird. Es kann in mehrere Teile unterteilt werden, z. B. einen Zyklus zum Aktualisieren der Physik und einen Zyklus zum Verarbeiten der Spielereingaben.
Definieren eines Zustands und einer Zustandsmaschine
Gehen Sie zu
RW / Scripts und öffnen Sie
StateMachine.cs .
Die Zustandsmaschine stellt , wie Sie vielleicht vermuten, eine Abstraktion für die Zustandsmaschine bereit. Beachten Sie, dass sich
CurrentState korrekt in dieser Klasse befindet. Es wird eine Verknüpfung zum aktuellen Status der aktiven Statusmaschine gespeichert.
Um das Konzept des
Status zu definieren,
rufen Sie RW / Scripts auf und öffnen Sie das Skript
State.cs in der IDE.
State ist eine abstrakte Klasse, die wir als
Modell verwenden, aus dem alle
Klassen von Projektstatus abgeleitet werden. Ein Teil des Codes in den Projektmaterialien ist bereits fertig.
DisplayOnUI zeigt nur den Namen des aktuellen Status in der Bildschirmbenutzeroberfläche an. Sie müssen das interne Gerät nicht kennen. Sie müssen lediglich verstehen, dass es einen Enumerator des Typs
UIManager.Alignment als Eingabeparameter empfängt, der
Left oder
Right . Die Anzeige des Namens des Status im linken oder rechten unteren Teil des Bildschirms hängt davon ab.
Zusätzlich gibt es zwei geschützte Variablen,
character und
stateMachine . Die
character verweist auf eine Instanz der
Character- Klasse, und
stateMachine verweist auf eine Instanz
der Zustandsmaschine, die dem Zustand zugeordnet ist.
Beim Erstellen einer
stateMachine bindet der Konstruktor
character und
stateMachine .
Jede der vielen Instanzen von
Character in einer Szene kann einen eigenen Satz von Zuständen und Zustandsautomaten haben.
Fügen Sie State.cs nun die folgenden Methoden
hinzu und speichern Sie die Datei:
public virtual void Enter() { DisplayOnUI(UIManager.Alignment.Left); } public virtual void HandleInput() { } public virtual void LogicUpdate() { } public virtual void PhysicsUpdate() { } public virtual void Exit() { }
Diese virtuellen Methoden definieren die oben beschriebenen Hauptstatuspunkte. Wenn
die Zustandsmaschine einen Übergang zwischen Zuständen durchführt, rufen wir
Exit für den vorherigen Zustand auf und
Enter neuen
aktiven Zustand ein .
HandleInput ,
LogicUpdate und
PhysicsUpdate definieren zusammen
eine Aktualisierungsschleife .
HandleInput verarbeitet die Player-Eingabe.
LogicUpdate verarbeitet grundlegende Logik, während
PhyiscsUpdate Logik- und Physikberechnungen verarbeitet.
Öffnen
Sie nun
StateMachine.cs erneut, fügen Sie die folgenden Methoden hinzu und speichern Sie die Datei:
public void Initialize(State startingState) { CurrentState = startingState; startingState.Enter(); } public void ChangeState(State newState) { CurrentState.Exit(); CurrentState = newState; newState.Enter(); }
Initialize konfiguriert den Zustandsautomaten, indem
CurrentState auf
startingState und die
Enter dafür
CurrentState . Dies initialisiert die Zustandsmaschine und setzt zum ersten Mal den aktiven Zustand.
ChangeState behandelt
ChangeState . Es ruft
Exit für den alten
CurrentState bevor die Referenz durch
newState . Am Ende wird
Enter für
newState .
So setzen wir den
Zustand und die
Zustandsmaschine .
Bewegungszustände erstellen
Schauen Sie sich das folgende Zustandsdiagramm an, in dem die verschiedenen
Bewegungszustände der In-Game-Essenz
des Spielers dargestellt sind. In diesem Abschnitt implementieren wir die Vorlage "Status" für die in der
FSM- Abbildung gezeigte
Bewegung :
Achten Sie auf die Bewegungszustände
Stehen ,
Ducken und
Springen sowie darauf, wie die eingehenden Daten Übergänge zwischen den Zuständen verursachen. Dies ist eine hierarchische FSM, in der
Grounded ein Unterzustand für die
Unterzustände Ducking und
Standing ist .
Kehren Sie zu Unity zurück und gehen Sie zu
RW / Scripts / States . Dort finden Sie mehrere C # -Dateien mit Namen, die auf
State enden.
Jede dieser Dateien definiert eine Klasse, von denen jede von
State geerbt wird. Daher definieren diese Klassen die Zustände, die wir im Projekt verwenden werden.
Öffnen Sie nun
Character.cs aus dem
RW / Scripts- Ordner.
#region Variables über die Datei
#region Variables und fügen Sie den folgenden Code hinzu:
public StateMachine movementSM; public StandingState standing; public DuckingState ducking; public JumpingState jumping;
Diese
movementSM bezieht sich auf eine Zustandsmaschine, die die Bewegungslogik für die
Character Instanz verarbeitet. Wir haben auch Links zu drei Zuständen hinzugefügt, die wir für jede Art von Bewegung implementieren.
Gehen
#region MonoBehaviour Callbacks in derselben Datei zu
#region MonoBehaviour Callbacks . Fügen Sie die folgenden
MonoBehaviour- Methoden hinzu und speichern Sie sie
private void Start() { movementSM = new StateMachine(); standing = new StandingState(this, movementSM); ducking = new DuckingState(this, movementSM); jumping = new JumpingState(this, movementSM); movementSM.Initialize(standing); } private void Update() { movementSM.CurrentState.HandleInput(); movementSM.CurrentState.LogicUpdate(); } private void FixedUpdate() { movementSM.CurrentState.PhysicsUpdate(); }
- In
Start Code eine Instanz der Zustandsmaschine und weist sie movementSM . Außerdem werden verschiedene Bewegungszustände instanziiert. Bei der Erstellung der einzelnen Bewegungszustände übergeben wir unter Verwendung des this sowie der movementSM Instanz Verweise auf die Character Instanz. Am Ende rufen wir Initialize for movementSM und übergeben Standing als Ausgangszustand. - In der
Update Methode rufen wir HandleInput und LogicUpdate für den CurrentState der movementSM Maschine auf. Ebenso FixedUpdate wir in FixedUpdate PhysicsUpdate für den CurrentState der movementSM Maschine auf. Im Wesentlichen delegiert dies Aufgaben an einen aktiven Status. Dies ist die Bedeutung der Vorlage "Status".
Jetzt müssen wir das Verhalten in jedem der Bewegungszustände einstellen. Machen Sie sich bereit, es wird eine Menge Code geben!
Stehend fest
Kehren Sie im Projektfenster zu
RW / Scripts / States zurück.
Öffnen Sie
Grounded.cs, und beachten Sie, dass diese Klasse über einen Konstruktor verfügt, der dem
State Konstruktor entspricht. Das ist logisch, weil diese Klasse davon erbt. Sie werden dasselbe in allen anderen Staatsklassen sehen.
Fügen Sie den folgenden Code hinzu:
public override void Enter() { base.Enter(); horizontalInput = verticalInput = 0.0f; } public override void Exit() { base.Exit(); character.ResetMoveParams(); } public override void HandleInput() { base.HandleInput(); verticalInput = Input.GetAxis("Vertical"); horizontalInput = Input.GetAxis("Horizontal"); } public override void PhysicsUpdate() { base.PhysicsUpdate(); character.Move(verticalInput * speed, horizontalInput * rotationSpeed); }
Folgendes passiert hier:
- Wir definieren eine der in der Elternklasse definierten virtuellen Methoden neu. Um die gesamte Funktionalität des übergeordneten Elements beizubehalten, rufen wir die
base mit demselben Namen für jede überschriebene Methode auf. Dies ist eine wichtige Vorlage, die wir weiterhin verwenden werden. - Die nächste Zeile,
Enter setzt horizontalInput und verticalInput ihre Standardwerte. - Innerhalb von
Exit rufen wir, wie oben erwähnt, die ResetMoveParams Methode des , um sie zurückzusetzen, wenn Sie in einen anderen Status wechseln. - In der
HandleInput Methode zwischenspeichern die Variablen horizontalInput und verticalInput HandleInput Werte der horizontalen und vertikalen Eingabeachse. Dank dessen kann der Spieler den Charakter mit den Tasten W , A , S und D steuern . - Bei
PhysicsUpdate wir Move und übergeben die Variablen horizontalInput und verticalInput multipliziert mit den entsprechenden Geschwindigkeiten. In der variablen speed die Bewegungsgeschwindigkeit und in rotationSpeed die Winkelgeschwindigkeit gespeichert.
Öffnen Sie nun
Standing.cs und achten Sie darauf, dass es von
Grounded erbt. Es ist passiert, weil
Standing , wie wir oben sagten, ein Substate für
Grounded ist . Es gibt verschiedene Möglichkeiten, diese Beziehung zu implementieren. In diesem Lernprogramm wird jedoch die Vererbung verwendet.
Fügen Sie die folgenden
override und speichern Sie das Skript:
public override void Enter() { base.Enter(); speed = character.MovementSpeed; rotationSpeed = character.RotationSpeed; crouch = false; jump = false; } public override void HandleInput() { base.HandleInput(); crouch = Input.GetButtonDown("Fire3"); jump = Input.GetButtonDown("Jump"); } public override void LogicUpdate() { base.LogicUpdate(); if (crouch) { stateMachine.ChangeState(character.ducking); } else if (jump) { stateMachine.ChangeState(character.jumping); } }
- In
Enter konfigurieren wir die von Grounded geerbten Variablen. Wenden Sie die MovementSpeed und RotationSpeed Charakters auf speed und rotationSpeed . Dann beziehen sie sich jeweils auf die normale Bewegungsgeschwindigkeit und die Winkelgeschwindigkeit, die für das Wesen des Charakters bestimmt sind.
Darüber hinaus werden Variablen zum Speichern von crouch Eingaben und jump auf false zurückgesetzt. - In
HandleInput speichern die Variablen HandleInput und jump die Eingaben des Spielers für Kniebeugen und Sprünge. Wenn der Spieler in der Hauptszene die Umschalttaste drückt , wird die Hocke auf true gesetzt. Ebenso kann ein Spieler mit der Leertaste jump . - In
LogicUpdate überprüfen wir die Variablen LogicUpdate und jump vom Typ bool . Wenn crouch true ist, ändert sich movementSM.CurrentState in character.ducking . Wenn jump true ist, ändert sich der Status in character.jumping .
Speichern und montieren Sie das Projekt und klicken Sie dann auf
Wiedergabe . Mit den Tasten
W ,
A ,
S und
D können Sie sich in der Szene bewegen
. Wenn Sie versuchen, die
Umschalt- oder Leertaste zu drücken, tritt ein unerwartetes Verhalten auf, da die entsprechenden Status noch nicht implementiert sind.
Versuchen Sie, sich unter den Tischobjekten zu bewegen. Sie werden sehen, dass dies aufgrund der Höhe des Colliders des Charakters nicht möglich ist. Damit der Charakter dies tun kann, müssen Sie das Verhalten der Hocke hinzufügen.
Wir klettern unter den Tisch
Öffnen Sie das Skript
Ducking.cs . Beachten Sie, dass
Ducking aus den gleichen Gründen wie
Standing auch von der
Grounded Klasse erbt. Fügen Sie die folgenden
override und speichern Sie das Skript:
public override void Enter() { base.Enter(); character.SetAnimationBool(character.crouchParam, true); speed = character.CrouchSpeed; rotationSpeed = character.CrouchRotationSpeed; character.ColliderSize = character.CrouchColliderHeight; belowCeiling = false; } public override void Exit() { base.Exit(); character.SetAnimationBool(character.crouchParam, false); character.ColliderSize = character.NormalColliderHeight; } public override void HandleInput() { base.HandleInput(); crouchHeld = Input.GetButton("Fire3"); } public override void LogicUpdate() { base.LogicUpdate(); if (!(crouchHeld || belowCeiling)) { stateMachine.ChangeState(character.standing); } } public override void PhysicsUpdate() { base.PhysicsUpdate(); belowCeiling = character.CheckCollisionOverlap(character.transform.position + Vector3.up * character.NormalColliderHeight); }
- In der
Enter Parameter, der das Umschalten der Kniebeugenanimation bewirkt, auf "geduckt" gesetzt, wodurch die Kniebeugenanimation aktiviert wird. Den Eigenschaften character.CrouchSpeed und character.CrouchRotationSpeed die Werte für speed und rotation zugewiesen, die die Bewegung und Winkelgeschwindigkeit des Zeichens beim Bewegen in einer Hocke zurückgeben .
Nächstes character.CrouchColliderHeight legt die Größe des Colliders des Zeichens fest, der beim Hocken die gewünschte Collider-Höhe zurückgibt. Am Ende wird belowCeiling auf false zurückgesetzt. - Innerhalb von
Exit der Parameter für die Squat-Animation auf false festgelegt. Dadurch wird die Squat-Animation deaktiviert. Dann wird die normale Collider-Höhe festgelegt, die vom character.NormalColliderHeight zurückgegeben wird. character.NormalColliderHeight . - In
HandleInput Variable crouchHeld den Eingabewert des Players fest. crouchHeld in der crouchHeld die Umschalttaste crouchHeld , wird crouchHeld auf true gesetzt. - In
PhysicsUpdate Variablen belowCeiling ein Wert zugewiesen, indem ein Punkt im Vector3 Format mit dem Kopf des Vector3 des Charakters an die CheckCollisionOverlap Methode übergeben wird. Wenn es in der Nähe dieses Punktes zu einer Kollision kommt, bedeutet dies, dass sich der Charakter unter einer bestimmten Decke befindet. - Intern prüft
LogicUpdate , ob crouchHeld oder belowCeiling wahr ist. Wenn keines davon wahr ist, ändert sich movementSM.CurrentState in character.standing .
Erstellen Sie das Projekt und klicken Sie auf
Abspielen . Jetzt können Sie sich in der Szene bewegen. Wenn Sie die
Umschalttaste drücken, setzt sich der Charakter hin und Sie können sich in der Hocke bewegen.
Sie können auch unter der Plattform klettern. Wenn Sie die
Umschalttaste unter den Plattformen loslassen, befindet sich der Charakter immer noch in der Hocke, bis er sein Obdach verlässt.
Steig auf!
Öffnen Sie
Jumping.cs . Sie sehen eine Methode namens
Jump . Mach dir keine Sorgen darüber, wie es funktioniert; Es reicht zu verstehen, dass es verwendet wird, damit der Charakter unter Berücksichtigung von Physik und Animation springen kann.
Fügen Sie nun die üblichen
override und speichern Sie das Skript
public override void Enter() { base.Enter(); SoundManager.Instance.PlaySound(SoundManager.Instance.jumpSounds); grounded = false; Jump(); } public override void LogicUpdate() { base.LogicUpdate(); if (grounded) { character.TriggerAnimation(landParam); SoundManager.Instance.PlaySound(SoundManager.Instance.landing); stateMachine.ChangeState(character.standing); } } public override void PhysicsUpdate() { base.PhysicsUpdate(); grounded = character.CheckCollisionOverlap(character.transform.position); }
- Innerhalb von
Enter Singleton SoundManager den Sound des Sprungs ab. Dann wird grounded auf den Standardwert zurückgesetzt. Am Ende heißt Jump . - In
PhysicsUpdate Punkt neben den Beinen des Charakters an CheckCollisionOverlap gesendet. Wenn sich der Charakter auf dem Boden befindet, wird grounded auf True gesetzt. LogicUpdate in LogicUpdate grounded True ist, rufen wir TriggerAnimation auf, um die TriggerAnimation zu aktivieren, ein Landegeräusch wird abgespielt und movementSM.CurrentState ändert sich in character.standing .
Damit haben wir die vollständige Implementierung der FSM-Verschiebung mithilfe
der Vorlage „State“ abgeschlossen . Erstellen Sie das Projekt, und führen Sie es aus. Drücken Sie die
Leertaste , um den Charakter zum Springen zu bringen.
Wohin als nächstes?
Die
Projektmaterialien haben einen Projektentwurf und ein fertiges Projekt.
Trotz ihrer Nützlichkeit weisen Zustandsmaschinen Einschränkungen auf. Concurrent State Machines und Pushdown-Automaten können einige dieser Einschränkungen umgehen. Sie können darüber im Buch von Robert Nystrom
Game Programming Patterns nachlesen.
Darüber hinaus kann das Thema vertieft werden, indem die
Verhaltensbäume untersucht werden, mit denen komplexere Objekte im Spiel erstellt werden.