diff --git a/state/CA1.md b/state/CA1.md new file mode 100644 index 0000000..a91b33c --- /dev/null +++ b/state/CA1.md @@ -0,0 +1,218 @@ +# GAI/CA1 - Yan Wittmann + +## 1.1 Part 1: State Machines + + +* [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) + + +### 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": "" + }, + "template_transitions": { + "": {} + }, + "states": { + "": { + "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": "" | { "type": "" }, + "signal": "", + "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": "", +} +``` + +- 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": "", + "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. diff --git a/state/res/state-machine-demo.mp4 b/state/res/state-machine-demo.mp4 new file mode 100644 index 0000000..f86d202 Binary files /dev/null and b/state/res/state-machine-demo.mp4 differ