"Exodus from Eden" in Godot - Wie ich RPG Maker Mechaniken und mehr in Godot umsetze

  • Hallo, alle miteinander.


    Nach langer Zeit habe ich mich mal wieder dazu aufgerafft, einen Beitrag hier im Forum zu erstellen. Da habe ich direkt auch etwas Nettes vorbereitet.:)


    Wie ich zu Godot gekommen bin?

    Durch mein Studium sollte ich in einer Gruppe ein Lernspiel erstellen. Dies sollten wir mit der Game Engine Godot machen. Schnell merkte ich, dass ich diese Engine sehr mag und ich zumindest sehr intuitiv finde. Vor allem mag ich die Skriptsprache GDScript, die einem etwas mehr Sicherheit bietet als JavaScript. Da bin ich zu dem Entschluss gekommen, mit Godot mehr zu entwickeln. Da ich aber viele Umsetzungen des RPG Makers sehr gut finde, vor allem aber das Event-System auf der Map und so weiter, wollte ich einige Mechaniken des Makers in die Godot-Engine übertragen. Ich will also eine gewisse Programm-Architektur entwickeln, die es mir erlaubt, relativ einfach ein RPG nach meinen Vorstellungen zu erstellen.

    Wer weiß, vielleicht gibt es ja ein paar Interessenten, die meinen Prozess hier ebenfalls anschauen wollen oder vielleicht selbst Inspiration erhalten, etwas in Godot umzusetzen.


    Was will ich hier dann eigentlich präsentieren?

    Ich will über die Zeit hinweg einige Ansätze vorstellen, die ich angedacht habe, um die Entwicklung eines RPG's in Godot zu ermöglichen. Ich habe natürlich viel vor mir, aber das bin ich gewohnt.^^


    Teil 1: Der Aufbau und Wechsel von Szenen durch Module

    Damit man überhaupt etwas sieht, braucht man in Godot ja nicht viel. Man legt eine Szene an und startet das Spiel...Ach wenn es so einfach wäre, würde ich hier nicht schreiben. Es gibt da nämlich ein Problem: Wie kommt von einer Szene zu nächsten? Darüber hatte ich mir Gedanken gemacht. Im RPG-Maker werden ja Szenen auf einen Szenenstack gelegt, um auch so etwas wie Menüs oder Untermenüs zu erstellen. Dieser Ansatz ist sehr nett, doch ich wollte etwas anderes probieren. Ich wollte nämlich die Baumstruktur nutzen, die einen Godot mit dem Szenenbaum bietet. Dafür habe ich dann das Konzept der Module erarbeitet.



    Was ist ein Modul?

    Ein Modul ist hierbei eine Art Objekt, welches eine Szene darstellt und den bequemen Wechsel zwischen diesen Szenen erlaubt. Dabei kann ein Modul Kinder-Module haben, die sich ebenfalls abwechseln können. Damit kann man eine gewisse Ordnung aufbauen, die die Organisation des gesamten Spiels helfen kann. Nun werde ich euch am besten den Code mal zeigen.


    Da haben wir einen einleitenden Teil. Hierbei erweitert die Module-Klasse die Basisklasse Node. Weiterhin wird der Klassenname Module im Projekt global sichtbar gemacht, um Untertypen dieser Klasse entwickeln zu können. Am Ende haben wir hier eine exportierte Variable, in der die Kind-Module eingetragen werden können. Das ermöglicht uns folgende Hilfestellung in der Engine selbst:


    Wie ihr sehen könnt, kann man damit Kind-Module mit eigenen Namen versehen und den Pfad angeben, der zu diesem Modul führt. Die kann man dann einfach in seinem Datei-System aufrufen.


    Eine wichtige andere Funktion ist dann hierbei noch die Funktion activate_child()

    Sie erlaubt es, dass ein Modul das aktive Kind gegen ein anderes tauschen kann. Zunächst wird das neue Kind aktiviert und falls es ein vorheriges aktives Kind gab, so wird es zerstört.

    Wie würde so ein Prozess dann aber visuell aussehen.

    Ich habe mir diese Struktur überlegt. Hierbei startet Main alles. Hierbei hat Main die Kinder SplashScreen und Window, die es frei verwalten kann. In meinem Fall wird zuerst der SplashScreen aktiviert und dann kommt es zu einem Wechsel zu Window, worin das eigentliche Hauptspiel stattfindet. Hierbei wird der SplashScreen wieder zerstört. Gleich darauf wird das Menu aktiviert, wodurch auch das Menu sichtbar wird. Geht man dann auf New Game, folgt der Wechsel zur World, wo dann Spielinhalt wäre wie Maps usw.


    Hier nochmal das Ergebnis. Ist jetzt visuell nicht sehr ansprechend, demonstriert aber sehr gut den Ablauf.


    Naja, ich hoffe mal, dass ich meinen Ansatz einigermaßen erklären konnte. Hat echt eine Weile gedauert, sich das Ganze auszudenken. Was sagt ihr dazu? Wünscht ihr euch mehr Teile, wo ich erkläre, wie ich weitere Dinge umsetze, z.B. Aufbau von Charakterprofilen und Battlerprofilen ähnlich der RPG-Maker-Datenbank? Schreibt es gerne hier rein.:)

  • Gerade da das Visual Scripting mit Godot 4.0 erst mal raus genommen werden soll und eventuell nochmal über die Community eingepflegt wird, finde ich es cool wenn Du Dich da an eine Art "Event-System" orientierst, wie man es unter anderem aus der RPG-Maker-Reihe kennt. :)
    (Zumindest war das meine erste Assoziation, als Du Mechaniken des RPG-Makers erwähntest.)

    In Sachen UI-Homogenität wirst Du sicherlich auch nochmal massiv von den parent-nodes/"modules" profitieren. Generell ist der Workflow echt cool und ein wenig "trauere" ich dem auch nach, da ich mich aktuell aufgrund meiner visuelleren Ausprägungen mehr in Richtung "no-code" bewegt habe und mit GDevelop rumbastle (soweit die Zeit es zulässt). Der Animationen- und auch der Partikeleditor sind verdammt mächtig und könnten Deiner Arbeit sicherlich nochmal ein schönes Alleinstellungsmerkmal bescheren, wenn Du die späteren Möglichkeiten mit dem RPG-Maker vergleichen möchtest.

    "Menü" würde ich in Deiner Aufstellung zur besseren Verständlichkeit eher gegen "UI" (im Allgemeinen) austauschen. So sind imo die eher technisch-informativen und die eher rein-visuellen Inhalte für den Anwender besser nachvollziehbar unterteilt.
    Was ich mir aber auch gut vorstellen könnte wäre, wenn man verschiedene Unterarten von "Menü" im Zusammenspiel mit World definiert, welche dann bestimmte Arten von Spielszenen repräsentieren. Seien es jetzt Spielabschnitte mit (individuellen) Minispielen, reine Cutscenes, ein Kampfsystem in einer seperaten Szene oder beispielsweise die klassische Top-Down-Navigation durch eine Spielwelt. Wenn es da bestimmte Voreinstellungen gibt (gerade in Sachen unterschiedlicher Steuerungselemente), dürften die Szenen etwas schlanker bleiben und man kann mögliche Fehlerquellen (bei Konflikten oder eigentlich ungenutzten Elementen) etwas reduzieren.

    Vielleicht (wenn nicht schon geschehen) ist ja auch der Austausch mit Grandro mehr als fruchtbar, da er ebenfalls ein paar coole kleine Systeme in seinem Godot-Projekt verbaut hat.

    Ich hatte jetzt ein paar Versuche unternommen, aber kann es sein dass das gif nicht abgespielt werden kann?

  • Hallo, vielen Dank für das Feedback. Ja, eine Umbenennung von Menus zu UI ist natürlich möglich. Dieses Menus Modul sollte solche Module wie den Titelbildschirm, Optionen-Fenster und Organisations-Screens (Items, Characters, Skills, usw), wie man es vom Maker ähnlich kennt. Da das alle solche typischen Menüs sind, wollte ich das alles unter diesem Module zusammenfassen. Es sind also alles eigenständige Fenster, die nicht wirklich mit World-Modulen arbeiten. Andere UI's, die mehr mit der Umgebung in der Spielwelt zu tun haben, sind dann im World-Module zu finden. Deshalb kam es zu dieser Benennung letztenendlich.


    Mit Grandro hatte ich mich bezüglich Godot noch nicht ausgetauscht. Könnte ich vielleicht irgendwann mal machen.:)

  • Ah okay - ich hatte es so verstanden, dass es in etwa so wie zwei zusammen gehörige Layer pro "Map" ablaufen soll - deswegen die visuelle Aufteilung auf gleicher Höhe. :)

    Was die Menüs betrifft, kannst Du ja ausprobieren wie rund es am Ende wird. Meine Anregungen waren da auch eher auf hohem Niveau angesiedelt, da solche Ausnahmeerscheinungen wie Konflikte eher eine Seltenheit darstellen sollten.

  • Teil 2: InputMapping und InputHandler - Umgang mit Eingaben des Nutzers

    Im letzten Teil habe ich bereits kurz über die Modul-Mechanik gesprochen und wie man darüber die angezeigten Szenen relativ bequem wechseln kann. Damit sieht man dann im besten Fall eine nette World-Map mit einem Spieler-Sprite darauf... .... Hm, nett, man sieht einen Spieler, aber es wäre schon irgendwie nett, wenn der Spieler sich bewegen könnte. Dafür bieten sich Maus und Tastatur doch an, wie man es von klassischer RPG-Maker-Manier kennt. Doch wie kommen diese ganzen Eingaben auch bei der Spielfigur an?

    Godot bietet hierbei für die meisten Objekte die Funktion _input(event) an. Diese Funktion stets dann aufgerufen, wenn eine Eingabe wie Mausbewegung oder Tastendruck erfolgen. Das heißt, man könnte Eingaben auch unterscheiden und so Aktionen bedingt der Eingabe auslösen.

    Man kann diese Funktion also theoretisch bei dem Skript des Spielers einfügen, um Standard-Bewegung zu erzeugen:

    func input(event):

       if event.is_pressed("move"): # Bewege den Spieler mit etwas CodeDamit ist es dann in der Theorie möglich, den Spieler zu bewegen. Doch es gibt ein Problem. Wenn zum Beispiel eine Dialog-Box erscheint, kann der Spieler sich noch immer bewegen, obwohl ein Dialog durchläuft. Man möchte nicht unbedingt, dass sowas in wichtigen Cutscenes passiert. Die Lösung wäre also, die Behandlung von Eingaben so zu verändern, sodass solche Dinge nicht mehr möglich sind.

    Hier kommt der InputHandler ins Spiel. Es handelt sich um eine eigene Klasse, die als System aufgefasst wird. Es wird also global eine einzige Version von diesem InputHandler erzeugt, der von überall beeinflusst werden kann.

    Hierbei besitzt der InputHandler eine Variable vom Typ InputMapping. Über den Typ InputMapping werde ich gleich noch mehr erklären.

    Mit dieser Variable wird also gesteuert, wie einkommende Eingaben behandelt werden sollen. Um solche Eingaben zu erhalten, wird hier ganz praktisch die von Godot bereitgestellte Funktion _input(event) verwendet. Hierbei wird das ankommende Event im InputMapping hinterlegt und die Input-Behandlung wird aufgerufen, was durch das call() passiert.

    Danach kommt es zur einer if-Abfrage. Hierbei kann eine Eingabe entweder passieren, also von der restlichen Engine für andere Dinge weiterverwertet werden, oder aber wird durch den Aufruf get_viewport().set_input_as_handled() als abgeschlossen markiert und nicht weiterverwendet.


    Doch wie kann man all dies sinnvoll mit dem Typ InputMapping umsetzen?

    Hierzu definieren wir einen neuen Typ InputMapping.

    Hierbei haben wir drei Variablen. handle_input_event speichert, wie auf ein Eingabe-Event reagiert werden soll.

    current_input_event wird durch den InputHandler wie bereits beschrieben auf das gerade ankommende Eingabe-Event gesetzt, also hält es einfach zeitweise dieses Eingabe-Event, um es zu verwenden.

    event_can_pass sagt aus, ob ein Event über die normale Behandlung noch weiter wandern darf.


    Damit lässt sich dann gut das Verhalten überschreiben und abhängig von angezeigter Szene definieren.

    Als Beispiel hätte ich ein Skript für eine TestMap:


    Hierbei wird die Eingabe direkt gesetzt, sobald diese Szene den Szenenbaum von Godot betritt. Man sieht hier deutlich, dass für handle_input_event eine Funktion angegeben wird, wo auf die Aktion "menu" und "left_click" reagiert wird. Auf die genauen Reaktionen auf diese beiden Eingaben gehe ich jetzt nicht ein, da das nicht wichtig für die heutige Thematik ist und eher Testzwecken dient.


    Das soll erstmal reichen. Es ist ein etwas abstrakteres Thema diesmal gewesen. Kritik und Fragen sind gern gesehen.^^ Manchmal ist es schwer, etwas genau zu erklären.

  • Mir ist nicht ganz klar, wie das so mit mehreren Instanzen funktionieren soll, die auf Input reagieren wollen, da dein InputHandler ja nur ein InputMapping gleichzeitig behandeln kann.
    Hättest du noch eine TestMap, bzw. was spielerisch vielleicht mehr Sinn ergibt, noch ein Pausemenü oder ähnliches welches sich öffnen und schließen können lassen sollte, würden die current_input_map jeweils gegenseitig überschreiben.
    Ist da der Plan immer genau eine Instanz zu haben, die auf Input reagieren soll? Wie ist es mit Logik, falls es einmal keine Instanz gibt, die auf Input reagieren will?
    Wie ist es mit den anderen _input Funktionen, falls das momentane InputMapping das Event nicht weiterlassen will, soll das Event wirklich als handled gesetzt werden nur weil eine Instanz vielleicht darauf reagiert hat?
    Wer soll neben InputHandler noch auf Input reagieren?

    Dieser Beitrag wurde bereits 1 Mal editiert, zuletzt von Grandro ()

  • Das sind ja einige Fragen, die da aufkommen, Grandro .


    Ich versuche, sie alle so gut es geht, zu beantworten.


    Mir ist nicht ganz klar, wie das so mit mehreren Instanzen funktionieren soll, die auf Input reagieren wollen, da dein InputHandler ja nur ein InputMapping gleichzeitig behandeln kann.

    Das ist grundsätzlich korrekt. Ein InputHandler kann momentan nur ein InputMapping behandeln, was aktuell in der Variable current_input_map lagert. Dies soll ja genau dazu dienen, dass Inputs nicht parallel irgendwelche anderen Aktionen hervorrufen, die gar nicht erwünscht sind. In der Regel will man mit Eingaben ja auch nur eine Sache kontrollieren, sei es eine Spielfigur, die Navigation im Menü oder das Weiterklicken in einem Dialog. Sofern man mehrere Figuren kontrollieren möchte, könnte man dann zum Beispiel ein Manager-Skript erstellen, welches die Input-Map umstellt, sodass diese Funktion dann alles nach Wunsch kontrollieren kann.


    Hättest du noch eine TestMap, bzw. was spielerisch vielleicht mehr Sinn ergibt, noch ein Pausemenü oder ähnliches welches sich öffnen und schließen können lassen sollte, würden die current_input_map jeweils gegenseitig überschreiben.

    Ja, momentan würde ich das so lösen, dass das Pausenmenü dies tatsächlich umschreiben würde. Da gäbe es dann die Lösung, dass ein Menü das vorherige Mapping zwischenspeichert und beim Schließen des Menüs wiederum das zwischengespeicherte Mapping wieder in den InputHandler einlagert.


    Ist da der Plan immer genau eine Instanz zu haben, die auf Input reagieren soll?

    Dies ist mein Plan, ja. Ich wollte es so konzipieren, um die Komplexität zu verringern. Ich denke, zum Teil ist mir das auch gelungen. Natürlich gibt es aber immer Raum zur Nachbesserung.

    Wie ist es mit Logik, falls es einmal keine Instanz gibt, die auf Input reagieren will?

    Im Normalfall wird es immer ein Mapping geben, aber das ist ein sehr guter Einwand. In dem Fall muss eine if-Anweisung herhalten, die bei keinem angegebenen Mapping einfach keinerlei Reaktion mehr zeigt, also einfach alle Inputs abfängt, damit keine unvorgesehenen Nebenwirkungen auftreten.


    Wie ist es mit den anderen _input Funktionen, falls das momentane InputMapping das Event nicht weiterlassen will, soll das Event wirklich als handled gesetzt werden nur weil eine Instanz vielleicht darauf reagiert hat?

    Ich habe es gerade nochmal geprüft. Es scheint so, dass die Funktion input in den anderen Nodes immer noch funktioniert. Man könnte also auch parallele Reaktionen umsetzen, allerdings sollte man das eher vermeiden, wenn man das InputHandler-System benutzt, um die Übersicht zu behalten. Ein Event kann man grundsätzlich auch vorbeilassen mit event_can_pass. Damit wird es nicht auf gehandled gesetzt und kann weitergehen, z.B. zum GUI


    Wer soll neben InputHandler noch auf Input reagieren?

    Generell habe ich geplant, dass der InputHandler alle Inputs managed, also alle Reaktionen steuert. Daneben gibt es an für sich nur noch die Godot-Standard-Reaktionen des GUI, also Buttons, die man anklicken kann oder ähnliches.


    Ich hoffe, ich konnte die vielen Fragen gut genug beantworten. Es war einiges, aber hat mir nochmal geholfen, ein wenig über meine Umsetzung zu reflektieren.^^

  • Danke für die Antworten :)

    Ja, momentan würde ich das so lösen, dass das Pausenmenü dies tatsächlich umschreiben würde. Da gäbe es dann die Lösung, dass ein Menü das vorherige Mapping zwischenspeichert und beim Schließen des Menüs wiederum das zwischengespeicherte Mapping wieder in den InputHandler einlagert.

    Das macht für mich nur bedingt Sinn. Wenn das Pausemenü nicht geöffnet ist, sollte es ja die Möglichkeit geben sich zu bewegen und gleichzeitig aber auch das Menü öffnen zu können. Das heißt, es ist auf jedenfall notwendig zwei InputMappinghintereinander zu verarbeiten, wenn du nicht sehr wilde Veränderungen an den Mappings selber vornehmen willst.

    Wie wäre es denn zum Beispiel mit einer Stack-Struktur in welches die Nodes ihr InputMapping hinterlegen können? Dann könntest du immer den Stack durcharbeiten bis du zu einem Mapping triffst, welches das Event nicht durchlassen will.

    Ich habe es gerade nochmal geprüft. Es scheint so, dass die Funktion input in den anderen Nodes immer noch funktioniert. Man könnte also auch parallele Reaktionen umsetzen, allerdings sollte man das eher vermeiden, wenn man das InputHandler-System benutzt, um die Übersicht zu behalten. Ein Event kann man grundsätzlich auch vorbeilassen mit event_can_pass. Damit wird es nicht auf gehandled gesetzt und kann weitergehen, z.B. zum GUI

    Stimmt, das habe ich nicht ganz durchdacht. Ich habe event_can_pass falsch interpretiert, da es in deinem Beispiel immer auf false ist. Wenn ich nicht falsch liege funktioniert der Input bei anderen Nodes noch, weil die InputEvents jeweils den SceneTree rückwärts durchgehen, und da dein InputHandler vermutlich? ein Singleton ist, ist er eins der obersten Nodes im SceneTree und behandelt das Event somit auch als eins der letzten.


    Freut mich, dass du mit meinen Fragen/Anmerkungen etwas anfangen kannst.

  • Das macht für mich nur bedingt Sinn. Wenn das Pausemenü nicht geöffnet ist, sollte es ja die Möglichkeit geben sich zu bewegen und gleichzeitig aber auch das Menü öffnen zu können. Das heißt, es ist auf jedenfall notwendig zwei InputMappinghintereinander zu verarbeiten, wenn du nicht sehr wilde Veränderungen an den Mappings selber vornehmen willst.

    Wie wäre es denn zum Beispiel mit einer Stack-Struktur in welches die Nodes ihr InputMapping hinterlegen können? Dann könntest du immer den Stack durcharbeiten bis du zu einem Mapping triffst, welches das Event nicht durchlassen will.

    Ja, über eine Stack-Struktur habe ich auch schon nachgedacht. Das wäre durchaus eine Änderung, die gut umsetzbar wäre und gleichzeitig die InputHandler-Struktur bewahren kann. Danke auf jeden Fall für den Vorschlag mit dem Stack.:)


    Ja, der InputHandler ist im Übrigen ein Singleton, sodass global ein Zugriff erfolgen kann.^^

  • Teil 3: SaveNode, SaveFile, SaveSystem - Wie man den Spielstand speichert

    Mittlerweile kann man von Szene zu Szene wechseln und der Spieler kann sich auch bewegen. Toll! Gehen wir jetzt mal davon aus, dass man in einer Variable gold festhält, wie viele Münzen man im Spiel gesammelt hat. Man sammelt also fleißig Münzen ein während des Spielens und dann schaltet man nach einer ordentlichen Spiele-Session das Spiel aus. Später schaltet man es ja wieder ein... ... WAS, 0 Münzen!!! WIE KANN DAS SEIN?!


    Tja, da wurde wohl nicht richtig abgespeichert. Wenn das Spiel aus geht, gehen auch alle Werte in Variablen verloren und werden beim erneuten Starten auf die ursprünglichen Werte zurückgesetzt. Damit man seinen Fortschritt also nicht verliert, brauchen wir eine Lösung, veränderte Werte (wie die variable gold) auf der Festplatte zu speichern. Godot bietet hierbei viele Möglichkeiten, soetwas zu bewerkstelligen. Die alle aufzuzählen, wäre aber hier zuviel des Guten. Eine Möglichkeit werde ich aber hier verlinken zu Godot-Dokumentation: https://docs.godotengine.org/d…ials/io/saving_games.html


    So, das war es grundsätzlich mit den Erklärungen. Falls ihr irgendwelche Fragen, Anmerkungen oder Kritik habt, könnt ihr sie gerne äußern.

  • Interessant zu sehen, wie du vorgehst.

    Habe selbst in den letzten Wochen angefangen ein neues Speichersystem zu gestalten und eine etwas andere vorgehensweise.


    Das mit dem Speichern als Resource würde ich aber nochmal überdenken, es ist zwar die einfachste Methode, aber nicht sicher, da man da beliebigen Code einbetten kann. ( https://github.com/godotengine/godot-proposals/issues/4925 )

    Besser wäre da Konvertierung zu und von json.

  • Ja, mir ist tatsächlich bekannt, dass das Speichern als Resource ein Risiko darstellen kann. Anfangs habe ich sogar probiert, das alles als JSON zu speichern. Allerdings gab es dabei ein paar Probleme mit komplexeren Objekten. Momentan funktioniert es so erstmal. Vielleicht werde ich es aber nochmal überarbeiten und nochmal auf JSON umsteigen.

  • Könntest du darauf etwas detailierter eingehen?

    Was waren die Probleme?


    Mein System speichert für die Umwandlung zu json alles als Dictionaries in Dictionaries in Dictionaries...

    Sprich die Überresourcen rufen beim speichern eine save_to_dict() Methode der Unterresourcen auf und umgekehrt beim laden load_from_dict().

  • Oh, mir ist gerade aufgefallen, dass ich gar nicht die JSON Variante genutzt habe.^^ Stattdessen habe ich vorher store_var und get_var genutzt. Die scheinen aber Probleme beim Speichern von eigenen Klassen-Typen zu haben. Ich habe das Skript jetzt nochmal auf JSON umgestellt. Jetzt funktioniert es so wie mit dem ResourceSaver aber halt als JSON. Damit wäre das Problem gelöst und ich bin froh, dass ich damit das Sicherheitsrisiko umgehen kann. Danke auf jeden Fall nochmal für den Hinweis. Ich kannte das Problem mit Resourcen nur grob, aber in dem von dir verlinkten GitHub-Issue konnte ich nochmal etwas genauer nachlesen.:)


    EDIT: Ja, es scheint doch nicht so gut zu funktionieren, da beim Laden aus den Dictionaries keine Objekte mehr werden, sofern sie in tieferen Ebenen liegen, also Objekte in Objekten

    Dieser Beitrag wurde bereits 1 Mal editiert, zuletzt von EFJ_Baron ()

  • Jo, das ganze muss man dann rekursiv oder iterativ lösen.


    Ein anderes Problem ist auch, dass json nicht zwischen float und int unterscheidet.

    Aus diesem Grund habe ich bei meinem Speichersystem auch die Methoden zum kovertieren in jeder Resource seperat eingebaut, caste die Typen explizit und baue die Objekte/Resourcen vor Ort wieder neu zusammen. (Bzw. wandle sie beim speichern in json konforme dicts um.)


    Allerdings hast du mit dem Speichern von Objekten auch einen anderen Ansatz. Bei mir werden nur Resourcen gespeichert welche die Daten von Objekten speichern und wiederherstellen, aber nicht die Objekte selbst. Die Obekte selbst werden von Spawnern/Factories beim laden neu erstellt.

    (Ich bin mir aber ziemlich sicher, dass diese Lösung von mir nicht gerade sehr elegant ist.)


    Edit:

    Könnte es sein, dass dein Problem in der Natur von json liegt?

    Schau mal hier unter "Data Types":

    https://en.wikipedia.org/wiki/JSON

    Um es kurz zu machen, du kannst Godot Objekte und Reourcen nicht direkt in Dictionaries packen, die als json gespeichert werden.


    Wenn du es doch versuchst, wird ein String einer internen ID statt dem Objekt selbst gespeichert.

    Also z.b. aus einem Array[Object] mit name 'objekte' wird als Dictionary z.B. { "objekte": ["Object#1523", "Object#1634", "Object#1739"] } usw.

    Und das kann Godot natürlich nicht wieder in ein Objekt umwandeln, da es nur ein String einer Referenz ist.

    Dieser Beitrag wurde bereits 1 Mal editiert, zuletzt von Lihinel ()

  • Teil 3.1: Optimierung des SaveSystems durch einen Serializer

    Dank der Hinweise von Lihinel habe ich mich nochmals mit dem SaveSystem auseinandergesetzt. Statt der json-Speichermethode nutze ich nun wieder die store_var Variante. Damit wird es auch möglich, Vector2 und ähnliche Godot-Klassen effektiv abzuspeichern. Da blieben nur noch ein paar Probleme bestehen: Die Umwandlung von Objekten in speicherbare Daten und die Umwandlung dieser Daten wieder zurück in verwendbare Objekte. Hierzu habe ich eine Hilfsklasse Serializer angelegt.


    Diese Klasse hat die zentralen Funktion serialize und deserialize. Dabei bleiben die Daten unverändert, sofern sie z.B. nur ints oder strings sind. Sind sie Arrays oder Dictionaries, so werden die einzelnen Einträge mit der selben Funktion untersucht und umgewandelt. Objekte werden in spezielle Dictionaries umgewandelt. Zusätzlich werden sie noch mit dem Attribut REF versehen. Sodass beim Deserialisieren Objekte mit selber REF id nicht doppelt erzeugt werden.


    Beim Deserialisieren wird ein ähnliches Prinzip nur umgekehrt angewendet.