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