Added some documentation
parent
b688b5c8f5
commit
7ac9bf90e1
|
@ -0,0 +1,218 @@
|
||||||
|
# GAI/CA1 - Yan Wittmann
|
||||||
|
|
||||||
|
## 1.1 Part 1: State Machines
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
* [Einleitung](#einleitung)
|
||||||
|
* [Szenen und States-Implementierung](#szenen-und-states-implementierung)
|
||||||
|
* [Konfiguration der Transitionen](#konfiguration-der-transitionen)
|
||||||
|
* [Bedingungen (`conditions`)](#bedingungen-conditions)
|
||||||
|
* [Transfer (`transfer`)](#transfer-transfer)
|
||||||
|
* [Dynamische Zielzustände](#dynamische-zielzustände)
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
|
### Einleitung
|
||||||
|
|
||||||
|
Dieses Projekt stellt eine State Machine dar, die in der Lage ist, zwischen unterschiedlichen Zuständen aufgrund von
|
||||||
|
komplexen Bedingungen zu wechseln.
|
||||||
|
Hier wird diese für einen Character Controller eingesetzt, sie ist aber generisch genug, um auch für andere Anwendungen
|
||||||
|
verwendet zu werden.
|
||||||
|
Die Konfiguration der States und Transitions erfolgt über eine JSON-Datei
|
||||||
|
[character_state_machine.json](assets/character_state_machine.json), die zur Laufzeit eingelesen wird.
|
||||||
|
|
||||||
|
Konkret wird hier ein Reinigungsroboter simuliert, der Müllsäcke einsammelt und zu einem Müllcontainer bringt.
|
||||||
|
Regelmäßig muss er auch seine Batterie aufladen und dafür zu einer Ladestation fahren.
|
||||||
|
|
||||||
|
Eine Video-Demo ist hier verfügbar: [state-machine-demo.mp4](res/state-machine-demo.mp4)
|
||||||
|
|
||||||
|
Alle bewertungsrelevanten Punkte sind erfüllt:
|
||||||
|
|
||||||
|
- Das Projekt implementiert eine State Machine, die einen nicht-Spieler-Charakter steuert.
|
||||||
|
- Der aktuelle Zustand ist mit "b" einblendbar.
|
||||||
|
- Weitere Informationen über den aktuellen Zustand und die Transitionen sind zur Laufzeit mit "b" einblendbar.
|
||||||
|
Weitere Contrubutors können im Code einfach hinzugefügt werden.
|
||||||
|
- Ein Node-Graph zeigt die Transitionen und deren Bedingungen, sowie den aktuellen Zustand an. Mit "a" einblendbar.
|
||||||
|
- Die Visualisierung der Zusatzinformationen kann über "a" und "b" aktiviert und deaktiviert werden.
|
||||||
|
|
||||||
|
### Szenen und States-Implementierung
|
||||||
|
|
||||||
|
Die Haupt-Szene ist [StateMachineWorld.tscn](scenes/state/StateMachineWorld.tscn).
|
||||||
|
Diese verwaltet unter anderem diese Insatanzen:
|
||||||
|
|
||||||
|
- [SMCharacter.gd](scenes/state/SMCharacter.gd): Der Character, auf den die State Machine angewendet wird.
|
||||||
|
- [StateMachine.gd](scenes/state/StateMachine.gd): Als child node des Characters hat die state machine die States als
|
||||||
|
ihre child nodes.
|
||||||
|
- Weitere Instanzen wie ein [TrashBin.tscn](scenes/state/TrashBin.tscn) oder [Battery.tscn](scenes/state/Battery.tscn).
|
||||||
|
|
||||||
|
States implementieren die [State.gd](scenes/state/State.gd)-Klassenschnittstelle, die die folgenden Methoden definiert:
|
||||||
|
|
||||||
|
```gdscript
|
||||||
|
func state_enter() -> void
|
||||||
|
func state_process(delta: float) -> void
|
||||||
|
func state_exit(new_state: State) -> void
|
||||||
|
```
|
||||||
|
|
||||||
|
Da die Transitionen ausschließlich über die JSON-Datei konfiguriert werden, sind die States selbst nur für die
|
||||||
|
Implementierung der Logik zuständig.
|
||||||
|
Ein Beispiel von [state_PickupTrash.gd](scenes/state/state_PickupTrash.gd), der sich auf den Müllsack zubewegt:
|
||||||
|
|
||||||
|
```gdscript
|
||||||
|
extends State
|
||||||
|
|
||||||
|
var pickup_trash_target: Vector2 = Vector2.ZERO
|
||||||
|
|
||||||
|
|
||||||
|
func state_enter():
|
||||||
|
var waste = state_machine.state_transfer_variables["waste"]
|
||||||
|
pickup_trash_target = waste.position
|
||||||
|
print(waste, " ", pickup_trash_target)
|
||||||
|
|
||||||
|
|
||||||
|
func state_process(delta: float) -> void:
|
||||||
|
character.move_towards(pickup_trash_target, delta)
|
||||||
|
character.move_and_slide()
|
||||||
|
```
|
||||||
|
|
||||||
|
Es wird hier bereits viel auf den Charakter ausgelagert, um die States möglichst einfach zu halten.
|
||||||
|
Das viel interresantere ist jedoch die Konfiguration der Transitionen, was im folgenden Kapitel beschrieben wird.
|
||||||
|
|
||||||
|
### Konfiguration der Transitionen
|
||||||
|
|
||||||
|
In der JSON-Datei [character_state_machine.json](assets/character_state_machine.json) werden die States und Transitions
|
||||||
|
konfiguriert.
|
||||||
|
Hierbeit ist das Format möglichst offen gehalten, um auch komplexere Bedingungen zu ermöglichen.
|
||||||
|
|
||||||
|
Auf den obersten Ebenen werden allgemein die folgenden Keys verlangt:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"start": {
|
||||||
|
"state": "<state_name>"
|
||||||
|
},
|
||||||
|
"template_transitions": {
|
||||||
|
"<transition_name>": {}
|
||||||
|
},
|
||||||
|
"states": {
|
||||||
|
"<state_name>": {
|
||||||
|
"transitions": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `start`: Bestimmt initiale Eigenschaften der State Machine. Eine Property `state` gibt den Startzustand an.
|
||||||
|
- `template_transitions`: Definieren Transitionen, die in `states` referenziert werden können, falls sie mehrfach
|
||||||
|
verwendet werden.
|
||||||
|
- `states`: Definieren die States und über `transitions` deren Transitionen.
|
||||||
|
|
||||||
|
Transitionen sind das Kernstück der Konfiguration und haben folgende Struktur:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"target": "<state_name>" | { "type": "<selector_type>" },
|
||||||
|
"signal": "<signal_name>",
|
||||||
|
"conditions": [],
|
||||||
|
"transfer": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `target`: Der Name des Zielzustands, zu dem die State Machine wechseln soll. Kann auch ein Objekt sein, das dynamisch
|
||||||
|
einen Zielzustand bestimmt.
|
||||||
|
- `signal`: (Optional) Ein Signalname, der von der State Machine seit dem letzten Tick empfangen werden muss, damit die Transition
|
||||||
|
berücksichtigt wird.
|
||||||
|
- `conditions`: Ein Array von Bedingungen, die alle erfüllt sein müssen, damit die Transition durchgeführt wird.
|
||||||
|
- `transfer`: Ein Objekt, das definiert, welche Daten beim Übergang in den neuen Zustand übergeben werden sollen.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"template": "<template_transition_name>",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Oder einfach eine Referenz auf eine `template_transition`.
|
||||||
|
|
||||||
|
#### Bedingungen (`conditions`)
|
||||||
|
|
||||||
|
Bedingungen sind ein Array von Objekten, die alle erfüllt sein müssen, damit eine Transition ausgeführt wird. Jede
|
||||||
|
Bedingung hat folgende Struktur:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "<condition_type>",
|
||||||
|
"left": {},
|
||||||
|
"right": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `type`: Der Typ der Bedingung ("=", "!=", ">", "<", ">=", "<=").
|
||||||
|
- `left`: Der linke Operand der Bedingung.
|
||||||
|
- `right`: Der rechte Operand der Bedingung.
|
||||||
|
|
||||||
|
Die Operanden können entweder eine `value` Property (direkter Wert), einen `accessor` (Pfad zu einer Variable) oder eine
|
||||||
|
`function` (Aufruf einer Funktion) verwenden.
|
||||||
|
|
||||||
|
- `value`: Ein direkter Wert (z.B. `{"value": 100}`).
|
||||||
|
- `accessor`: Ein Array von Strings, das einen Pfad zu einer Variable in der Szene oder der State Machine angibt (z.B.
|
||||||
|
`{"accessor": ["character", "battery_charge"]}`).
|
||||||
|
- `function`: Ein Objekt, das eine Funktion mit Argumenten aufruft (z.B. `{"function": "distance", "args": [...]}`).
|
||||||
|
|
||||||
|
Diese können beliebig verschachtelt werden.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "<=",
|
||||||
|
"left": {
|
||||||
|
"value": 100
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"accessor": [
|
||||||
|
"character",
|
||||||
|
"battery_charge"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Transfer (`transfer`)
|
||||||
|
|
||||||
|
Das `transfer`-Objekt ermöglicht es, Daten zwischen Zuständen zu übertragen. Es ist ein Objekt, bei dem der Key der Name
|
||||||
|
der Variable ist, und der Wert ein Accessor, der die Datenquelle angibt.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"transfer": {
|
||||||
|
"waste": {
|
||||||
|
"accessor": [
|
||||||
|
"signals",
|
||||||
|
"waste_detected",
|
||||||
|
"args",
|
||||||
|
"waste"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In diesem Beispiel wird die Variable `waste` des Zielzustands mit dem Wert des Arguments `waste` des Signals `waste_detected` befüllt.
|
||||||
|
|
||||||
|
#### Dynamische Zielzustände
|
||||||
|
|
||||||
|
Der `target` einer Transition kann auch ein Objekt sein, das dynamisch einen Zielzustand bestimmt.
|
||||||
|
Das folgende Beispiel zeigt, wie man in der Historie der Zustände zu einem vorherigen Zustand zurückkehrt,
|
||||||
|
aber gewisse Zustände dabei ignoriert:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"target": {
|
||||||
|
"type": "history_first",
|
||||||
|
"ignore": [
|
||||||
|
"RechargeBattery",
|
||||||
|
"GoToBattery"
|
||||||
|
],
|
||||||
|
"restore_transfer_variables": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `type`: Der Typ des dynamischen Ziels (hier: `history_first`).
|
||||||
|
- `ignore`: Eine Liste von Zuständen, die bei der Suche nach dem vorherigen Zustand ignoriert werden sollen.
|
||||||
|
- `restore_transfer_variables`: Ein Boolean, der angibt, ob die Transfervariablen dieses vorherigen Zustands ebenfalls
|
||||||
|
wiederhergestellt werden sollen.
|
Binary file not shown.
Loading…
Reference in New Issue