JirkaDellOro / Prima

Repository for the module "Prototyping interactive media-applications and games" at Furtwangen University
https://jirkadelloro.github.io/Prima
10 stars 17 forks source link

Deserialisieren von Components #39

Closed Robin-Sb closed 3 years ago

Robin-Sb commented 4 years ago

Jedes mal wenn ich einen etwas komplexeren Node mit mehreren Components deserialisieren will bekomme ich die Fehlermeldung:

Deserialization failed: Error: Component is marked singleton and can't be attached, no more than one allowed

Im constructor von Component wird isSingleton immer auf true gesetzt und da das Attribut readonly ist kann man es auch nicht überschreiben. Kann man komplexere Objekte entsprechend gar nicht deserialisieren oder gibt es da einen Umweg? Wenn ich eine ähnliche Struktur wie im Test habe, bei dem der Child keine eigenen Components hat funktioniert es, aber sobald ein Child ebenfalls Components hat schlägt das Deserialisieren fehl.

Hier ist mein Code: https://github.com/Robin-Sb/Platform_Editor/tree/master/src/Game Hier deployed auf pages: https://robin-sb.github.io/Platform_Editor/src/Game/Main.html Und hier der JSON-File den ich benutzt habe: https://github.com/Robin-Sb/Platform_Editor/blob/master/text.json

JirkaDellOro commented 4 years ago

Die Serialisierung wurde lange Zeit nicht gepflegt, da gibt es sicher einiges nachzubereiten. Ich kann mit deinen Daten den Fehler reproduzieren und schaue mir das an.

JirkaDellOro commented 4 years ago

Ich denke, das Problem ist mannigfaltig.

  1. Dein Enemy wird derzeit über die Standardmethoden der Superklasse Node de-/serialisiert. Dabei wird über alle Componenten iteriert und diese de-/serialisiert. Enemy aber erzeugt im Konstruktor selbst Komponenten, danach versucht der Serializer auch nochmal die gleichen Komponente anzuhängen. Das knackt dann. -> Enemy braucht eine eigene De-/Serialisierung, so ist das auch gedacht, dass bei komplexeren Gebilden diese Methoden gebaut werden. -> oder die Konstruktion der Komponenten wird aus dem Enemy-Konstruktor in eine eigene Methode verschoben, die vom Programm, das ursprünglich die Enemies erzeugt, explizit aufgerufen wird. Damit sollte dann auch die Standard-Serialisierung wieder laufen. Das ist ein weiteres Design-Muster. Derzeit sind an vielen Stellen die Konstruktoren zu schlau und werden sich mit der Serialisierung beißen. Bei der Standard-Serialisierung werden nämlich keine Konstruktorparameter berücksichtigt. -> man könnte auch überlegen, ob man NodeResource für den Enemy einsetzt, das ist dann aber wieder ein weitergehendes Thema

  2. Du solltest die Überlegen, ob dieser ComponentPicker, den ich mal für die Tower geschrieben hatte, wirklich sinnvoll ist. Viel besser ist sicher die neue 3D-Ray-Methode zu nutzen.

  3. es interessiert mich trotzdem, warum das singleton-Attribut nicht den Wert false erhält, den Du im Konstruktor von ComponentPicker definierst. Ich konnte kein readonly finden. Wo hast Du das gesehen?

Robin-Sb commented 4 years ago

1, Das hilft mir auf jeden Fall weiter, danke dafür!

  1. Das ist auch ein sehr guter Hinweis, ich habe das auch im Prototyp einfach mal so aus meinem Tower-Code kopiert, weil das eben am schnellsten ging, aber das werde ich auf jeden Fall ebenfalls ausbessern.

  2. Das singleton-Attribut wird auf false gesetzt, aber weil im Component nur active serialisiert wird, ist das éinfach nicht Teil der Serialisierung. Die entsprechende Methode in Component.ts: public serialize(): Serialization { let serialization: Serialization = { active: this.active }; return serialization; } Mit readonly meinte ich, dass singleton protected ist und ich das zum Beispiel nicht beim Erzeugen setzen kann (also zum Beispiel cmpMesh.singleton = false im Konstruktor der Enemy Klasse). Da habe ich mich falsch ausgedrückt. Ich könnte wohl eine neue Klasse erstellen, die von ComponentMesh erbt und dann singleton im Konstruktor auf false setzen und dann ebenfalls die serialize-Methode überschreiben, aber das kommt mir nach einer sehr schlechten Lösung vor.

JirkaDellOro commented 4 years ago

zu 3. Tatsächlich soll dies auch nicht serialisiert werden, da es eigentlich ein Klassenattribut sein müsste. Allerdings könnte es z.B. Subklassen von ComponentScript geben die Singletons sein sollen, und andere von denen mehrere Instanzen an einem Knoten hängen dürfen. Mesh oder Transform und einige andere darf es jedenfalls nur einmal geben, Light dagegen durchaus mehrfach.

Was ich noch nicht durchdrungen habe ist, dass ComponentPicker singleton als false definiert, das entstandene Objekt dann aber doch den Wert true aufweist. Any ideas?

Robin-Sb commented 4 years ago

Ja, das macht Sinn und so ähnlich hatte ich mir das auch vorgestellt.

Das ist mir auch gar nicht klar. Wenn ich das lokal debugge, ist singleton sogar false und wird auch mehrfach angehängt: single_false

Wenn ich die Version von Pages debugge, ist singleton wie bei dir auch true: single_true

Wenn ich zwei weitere Zeilen zum debuggen einfüge (hier der diff), dann ist singleton auch false (auch online) single_false2

Wenn ich die Zeilen wieder lösche ((hier wieder der diff), dann ist singleton wieder false und ich kann das gar nicht mehr reproduzieren. Vielleicht kannst du mal drüber schauen, ob das bei dir noch auftrifft nach den Änderungen? Das ist auf jeden Fall relativ seltsam und für mich nicht erklärbar.

Robin-Sb commented 4 years ago

Das Serialisieren/Deserialisieren von den Components funktioniert jetzt so weit gut, allerdings sind all meine Nodes weiß. Wenn ich das richtig sehe liegt das daran, dass in ComponentMaterial nur this.material serialisiert/deserialisiert wird und clrPrimary/clrSecondary nicht (welche standardmäßig weiß sind). Sollte ich diese Attribute in dem Fall nicht benutzen? Wenn ich mich richtig erinnere hattest du im Laufe das Semesters was daran geändert, könnte ja auch sein, dass du einfach vergessen hast, die neuen Attribute zu serialisieren.

Außerdem "musste" ich für die Serialisierung von den Resources den ResourceManager mit serialisieren (und das dann in zusammen in ein JSON file hacken), das funktioniert zwar soweit relativ gut, aber ich bin mir nicht sicher ob das die vorgesehene Lösung ist.

JirkaDellOro commented 4 years ago
  1. die Serialisierung wird derzeit nur bei Bedarf gepflegt, Du kannst also gerne vergessene Properties nachtragen.
  2. den ResourceManager zu serialisieren ist nicht vorgesehen, das geht sicher nach hinten los. Warum erscheint dir das erforderlich?
Robin-Sb commented 4 years ago
  1. Alles klar, mach ich. Danke!
  2. Bei mir ist standardmäßig idResource von Material gesetzt (ich habe ehrlich gesagt auf die Schnelle nicht gefunden, wo das gesetzt wird), weswegen in ComponentMaterial nur idMaterial serialisiert wird:
    let idMaterial: string = this.material.idResource;
    if (idMaterial)
    serialization = { idMaterial: idMaterial };

    Beim Deserialisieren schaut er dann automatisch nur in den ResourceManager:

    if (_serialization.idMaterial)
    material = <Material>ResourceManager.get(_serialization.idMaterial);

    Dort ist ResourceManager.serialization natürlich standardmäßig null, wenn ich den ResourceManager nicht mitserialisiere und

    let serialization: Serialization = ResourceManager.serialization[_idResource]; 

führt zu einem Error. Dadurch, dass nur idMaterial serialisiert wird fehlen natürlich sowieso die nötigen material-Informationen. Zum Vergleich der json-Teil:

   "ComponentMaterial": [
      {
         "ƒ.ComponentMaterial": {
               "idMaterial": "Material|2020-08-02T16:28:36.738Z|92439",
                  "Component": {
                     "active": true
                  }
          }
       }
    ]
JirkaDellOro commented 4 years ago

Also: der ResourceManager managed Ressourcen, also FUDGE-Objekte, die mehrfach Verwendung finden sollen. Sie werden über die id identifiziert, die automatisch vergeben wird. Das sollte auch funktionieren, es ist aber natürlich wichtig, dass die Resourcen auch serialisiert wurden. Schau mal in den Tests, da müsste es Beispielcode geben. Ich bin gerade dabei, den FUDGE-Editor entsprechend aufzurüsten, wodurch diese Automatismen besser sichtbar würden. Die nächsten zwei Wochen habe ich aber wenig Zeit dafür.

Robin-Sb commented 4 years ago

Ich habe mal https://jirkadelloro.github.io/FUDGE/Test/Transfer/Serializer/Test.html durchgesteppt, da holt der sich auch einfach die Information von ComponentMaterial über den ResourceManager. Das funktioniert natürlich auch, weil im gleichen Projekt serialisiert und deserialisiert wird, der ResourceManager ist also noch komplett im RAM. Bei mir wird in komplett unterschiedlichen Projekten deserialisiert und serialisiert und Informationen über die Objekte wird nur über die json files geteilt. Entsprechend ist der ResourceManager natürlich anfangs leer und der weiß nichts über die Resourcen von dem anderen Projekt.

Ich würde denke ich einfach mal für dieses Projekt meine Lösung mit dem Serialisieren vom ResourceManager lassen, im Prinzip wird ja nur der ResourceManager mit Startwerten initialisiert und bisher scheint das auch noch gut zu funktionieren.