Jump to content
Unity Insider Forum

AssetDataBase.SaveAssets speichert keine List<>


chrische5

Recommended Posts

Hallo

 

Ich habe mal wieder Probleme mit SaveAssets. Ich habe eine Klasse Quest, die von SO erbt. Innerhalb dieser Klasse gibt es eine List<Quest> also Subquests. Wenn ich den quest so abspeichern will:

AssetDatabase.CreateAsset(questToSave, path);
                AssetDatabase.SaveAssets();

Scheint er die Liste nicht mit zu speichern. Zumindest ist das entsprechende Feld beim Auslesen leer. Mache ich was falsch?

Ich zeige auch gern mehr Code, wusste nur nicht, welcher wichtig sein könnte.

 

Danke

Christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du begibst dich gerade in Furcht erregende Gefilde! Arr, ich habe sie die letzten Jahre auch immer wieder durchsegelt. Mache dich auf schreckliche Ungeheuer gefasst! 🏴‍☠️

Aber ernsthaft, nur so als Warnung: Ab jetzt sind Kopfschmerzen vorprogrammiert. Der Serializer ist nix für schwache Nerven 😀

In diesem Fall würde ich vermuten, dass deine Subquests nicht gespeichert werden. Die Liste wird also korrekt gepsiechert, die Elemente, die sie referenzieren soll, aber nicht. Damit werden die Referenzen ungültig - spätestens, wenn die Subquests flöten gegangen sind.

Was du hier machen willst ist, die Subquests als Sub-ScriptableObjects in dein Haupt-Quest-SO zu speichern. Dafür kannst du AssetDatabase.AddObjectToAsset benutzen. Sieht dann so aus, wie du es bestimmt schon z.B. bei 3D-Modellen oder Sprites gesehen hast, zum Aufklappen in den Assets. Mein Input-System nutzt das auch:

grafik.png

Mit der Methode muss man sich ein bisschen anfreunden, bevor die Ergebnisse stimmen. Es muss auch einiges drumherum gemacht werden. Achte zum Beispiel darauf, dass du nach AddObjectToAsset nochmal das Haupt-SO speicherst, denn die untergeordneten stecken in derselben Datei.

Usability-mäßig begibst du dich etwas in eine Welt des Schmerzes. Du machst zwei Dinge: 1. Du speicherst Objekte und 2. du speicherst eine Liste. Wenn du das in einem Schritt machst und dann Strg+Z drückst, dann wird standardmäßig nur eines davon rückgängig gemacht. So hast du entweder ein SO, das nicht mehr in der Liste referenziert wird oder wieder einen Listeneintrag, der nirgendwo hinzeigt. Du willst dich also etwas mit der Undo-Klasse auseinandersetzen um diese Aktionen zu atomaren Aktionen zu gruppieren. Wenn du dann irgendeinen Edge Case nicht beachtest, kannst du schon gerne mal in so ekelige Situationen kommen, wo du am besten mit dem VCS einen rest machst. Oder du baust etwas Editor-Code zum automatisierten Aufräumen. Alternativ musst du halt immer mega aufpassen beim Editieren und gibst das System niemandem ohne Schulung zum Arbeiten in die Hand :P

Die positive Seite: Wenn so ein Ding erstmal läuft, ist es ziemlich geil, damit Dinge im Editor zu bauen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo

 

Wow. Das hat erstmal echt geholfen. Die Subquest werden jetzt, genau wie du gesagt hast, gespeichert. Nun dreht sich mir aber der Kopf, wie ich diese wieder auslesen kann. Ich habe eine ObjectField, indem ich den Quest ziehen kann, um ihn zu editieren. Dabei sollen natürlich auch all seine Subquest eingelesen werden. Wie bekomme ich diese aus dem Parent heraus? 

 

Christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du willst vermutlich eher nicht zulassen, dass man die Liste überhaupt direkt editieren kann. Es ist am sinnvollsten, wenn die Liste immer genau alle Sub-SOs beinhaltet. Oder zumindest indirekt, wenn du Sub-Sub-Quests hast. Mein SO vom Screenshot oben hat in der Liste das WASD-Objekt stehen, das wiederum die anderen 4 Sub-SOs drin hat.

Du willst einen Layer Editor-Code zwischen den Benutzer und die Liste packen, sodass die Liste immer genau die SOs enthält und auch alle SOs in der Liste sind. Dafür willst du die Liste nicht direkt im Inspektor anzeigen (das geht mit [HideInInspector]), und deinen eigenen Editor-Code schreiben, um Elemente hinzuzufügen, umzusortieren oder zu löschen. Dafür bietet sich eine ReorderableList an. Ich glaube, dieser Artikel ist immer noch aktuell: https://va.lent.in/unity-make-your-lists-functional-with-reorderablelist/

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo

 

Doch ich will, dass der Nutzer die einzelnen Subquest editieren kann. Wegen mir auch löschen. Ich mache das jetzt mittels 

Object[] objs = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(asset));

und das klappt soweit ganz gut. 

Jetzt habe ich aber wieder das Problem, dass sich das SO leert, wenn ich Unity schließe. Das verstehe ich einfach nicht. Muss man Assets wie "abschließen", wenn man Unit beendet? Warum leert sich das komplette Asset? Alle Felder sind vorhanden, aber leer. Wenn ich die Sitzung offen lasse, kann ich das Asset immer wieder in das objectfield ziehen und bekomme die richtigen Werte angezeigt.

Hat jemand eine Idee? Das ganze läuft nicht im Playmode, sondern ausschließlich im Editor.

 

Christoph   

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb chrische5:

und das klappt soweit ganz gut. 

Joa, bis du halt versuchst, AssetDatabase in einem Build zu benutzen.

vor 2 Stunden schrieb chrische5:

Doch ich will, dass der Nutzer die einzelnen Subquest editieren kann. Wegen mir auch löschen.

Ja natürlich, aber du willst nicht, dass die Liste direkt editiert werden kann. Dein Code soll sicherstellen, dass alle Sub-SOs in der Liste sind und alle Listenelemente tatsächlich noch existieren. Deshalb baust du Editorcode wie die ReorderableList, mit denen der Nutzer die Objekte erstellen kann. Da passiert dann aber dein Code, der sicherstellt, dass die Liste immer topaktuell ist, anstatt dass da der normale Kram angezeigt wird, mit dem man die Liste direkt bearbeiten kann.

vor 2 Stunden schrieb chrische5:

Jetzt habe ich aber wieder das Problem, dass sich das SO leert, wenn ich Unity schließe. Das verstehe ich einfach nicht. Muss man Assets wie "abschließen", wenn man Unit beendet?

Ja, sind Dateien, die muss man speichern. AssetDatavase.SaveAssets regelt das bei mir gut.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo 

 

Ich versuche mal zu erklären, was ich meine. Ich habe ein kleines Fenster im. Editor und wenn ich dort auf add quest klicke, kann ich die entsprechenden Felder befüllen. Am Ende speichere ich das ganze mittels saveassets und im entsprechenden Ordner taucht das asset auf. Soweit, so gut. 

Es gibt auch ein objectfield und wenn ich dort ein quest reinziehe, dann kann man diesen editieren. Auch das klappt wunderbar. Nun schließe ich unity, öffne es wieder und ziehe den quest wieder in das Feld. Nun werden keine Daten angezeigt. 

Ich verstehe das njcht. 

 

Christoph 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo 

 

Das gleiche Problem hatte ich bereits vor ein, zwei Wochen. Keine Ahnung. Das Feld zum Editieren sollte ja nicht den Inhalt löschen. Zumal es ja funktioniert, wenn ich die Sitzung nicht schließe. Ich dreh durch... 😳

 

Weiß auch gar nicht, welchen Code ich zeigen könnte. Wenn jemand Lust auf Detektiv hat, kann ich gern den link zu github geben, dann kann er sich das mal anschauen. 

 

Christoph 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo

 

Peinlich ist es vielleicht.🙃 Wenn wirklich jemand Lust hat, freue ich mich natürlich. Das System ist noch sehr, sehr wackelig. Alles ist am Anfang. Ablauf ist folgender: Über das Menü und Window kann man QuestSystem auswählen. Daraufhin sollte sich ein Fenster öffnen. In diesem kann man eine neue Quest anlegen (mit Subquests, wenn man mag). Wenn man auf Save klickt, wird der Quest als asset im asset Ordner gespeichert. Nun kann man auf Clear all klicken und den gerade generierten Quest in das entsprechende Feld ziehen. -> alle Daten sind sichtbar. Wenn man nun Unity schließt, erneut öffnet und den gleichen Quest in das gleiche Feld zieht, bleibt alles leer. Zumindest ist das bei mir so.

 

Hier der Link zum Projekt. 

Link wegen Assets entfernt

 

Danke

Christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

So, hab da mal drüber geschaut. Das entscheidende Ding ist ja die QuestSystem-Klasse. Die wird dich übrigens daran hindern, zu builden, weil die nicht in einem Editor-Ordner drin ist. Dein Konzept in dieser Klasse ist ungewöhnlich, aber ich glaube, die Idee habe ich verstanden. Da der Ansatz wesentlich komplizierter (und nicht zuletzt einfach anders) ist als der, wie es vorgesehen ist, ist es schwer da den Fehler drin zu finden. Was du machst ist, im Editor-Code die Werte des Objekts zu setzen, das gerade bearbeitet wird. Das war bis Unity 3 oder so üblich seitdem aber nicht mehr. Seitdem ist es entsprechend immer schwieriger geworden, auf diese Art zu arbeiten, da halt alles drumherum weiterentwickelt wird.

Was du stattdessen machen willst, ist dir PropertyDrawer, SerializablePropertys und SerlaizableObjects anzuschauen.

Statt

var thing = (Thing)target;

thing.number = EditorGUILayout.IntField(thing.number);
thing.text = EditorGUILayout.StringField(thing.text);

macht man eher

serializedObject.Update();

EditorGUILayout.PropertyField(serializedObject.FindProperty("number"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("text"));

serializedObject.ApplyModifiedProperties();

Das bringt einem Sachen wie Multi-Object Editing, aber man spart sich auch eine Menge IMGUI-relatierte Schmerzen.

Dass du da ein Feld hast, wo du was reinziehst, würde ich auf jeden Fall rausnehmen. Auch eine potentielle Fehlerquelle. Stattdessen entweder den Inspektor verwenden - den gibt's gratis und da kann man genauso gut UI drin bauen - oder mit Selection.activeObject einfach schauen, was gerade markiert ist/wurde.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo

 

Verzeihung! Ich habe das Projekt entsprechend angepasst.

 

Zu deinem Post... Was ist denn aber das serializedObject, wenn ich einen neuen Quest anlege? Da gibt es ja noch nichts, was serializied wird. Das verstehe ich nicht.

Was für einen Inspektor meinst du?

 

Danke für deine Mühen!

Christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 10 Minuten schrieb chrische5:

Verzeihung! Ich habe das Projekt entsprechend angepasst.

Ich bin da nicht der Geschädigte ;)

Aber nur so als Hinweis: Der witz an Git ist, dass es eine Historie hat. Ist also alles noch da.

vor 11 Minuten schrieb chrische5:

Was für einen Inspektor meinst du?

Das Ding:

grafik.png

Du kannst das, was der Inspektor für deine Komponente oder dein ScriptableObject anzeigt, völlig frei gestalten.

vor 13 Minuten schrieb chrische5:

Zu deinem Post... Was ist denn aber das serializedObject, wenn ich einen neuen Quest anlege? Da gibt es ja noch nichts, was serializied wird. Das verstehe ich nicht.

Google doch erstmal nach dem Begriff: https://docs.unity3d.com/ScriptReference/SerializedObject.html

Da steht doch schon einiges drin. Aber in aller Kürze: Ein SerializedObject ist ein Objekt, das ein oder mehrere serialisierbare Objekte repräsentiert. ScriptableObjects fallen da genau wie GameObjects und Komponenten automatisch drunter. Deine Quests sind alle serialisiert, sonst würden sie ja auch nicht im Editor gespeichert werden können.

Wenn du einen CustomEditor (also eigenes Inspector-GUI) baust, dann hat der immer schon das SerializedObject parat, das entweder das eine oder eben sogar mehrere gleichartige Objekte repräsentiert. Deshalb kannst du im Editor auch mehrere Lichter markieren und sie alle auf einmal rot machen.

Das SerializedObject hat dann einen Enumerator über alle SerializedPropertys der Klasse, dessen Objekte repräsentiert werden. Stelle es dir wie eine "kaputte" Liste aller serialisierten Eigenschaften vor, also der Dinge, die public sind oder mit [SerializedField] markiert. Halt alles, was beim Default-Inspektor in der Liste auftaucht. Wenn man keinen Custom Inspector baut, dann rattert der stumpf die SerializedPropertys durch und zeigt die alle an. Wenn man aber einen baut, dann geht man zum SerializedObject, lässt sich von dem die Eigenschaften (SerializedProperty) geben und haut die in ein EditorGUI(Layout).PropertyField. Der zeigt dann die richtige Art Feld an (Nummer-Eingabe-Feld, Farbwähler, ...) und speichert die Änderungen direkt in die SerializedProperty.

Wenn du keinen Custom Inspector baust, sondern z.B. ein EditorWindow (was du ja tust), dann musst du dir ein SerializedObject besorgen, das das Ding repräsentiert, was du in deinem Fenster bearbeiten willst. Geht zum Glück lächerlich einfach:

var serializedObject = new SerializedObject(quest);

Und dann machst du Update, hast eine beliebige Anzahl PropertyFields und schließt mit ApplyModifiedProperties ab. Wie im vorherigen Post zu sehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo 

 

Ich konnte das jetzt noch nicht im Detail probieren. Ich möchte aber mal grundsätzlich sagen, dass deine Ausdauer und deine Art der Hilfestellung wirklich phantastisch sind. So viel neuer input, der trotzdem an bekannten Wissen anknüpft - das ist echt klasse. 

Für mich bedeutet das ganz konkret, dass mein Ansatz mit dem eigenen Editorwindow mir mittlerweile unpassend erscheint. Ich werde das über den Inspector lösen. 

Und dann werde ich mein altes Projekt neu aufrollen und dort deinen Einfluss gelten machen. 

Danke für die Erkenntnisse. 

 

Christoph 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 22 Minuten schrieb chrische5:

Ich möchte aber mal grundsätzlich sagen, dass deine Ausdauer und deine Art der Hilfestellung wirklich phantastisch sind. So viel neuer input, der trotzdem an bekannten Wissen anknüpft - das ist echt klasse. 

Danke, freut mich :)

vor 22 Minuten schrieb chrische5:

Und dann werde ich mein altes Projekt neu aufrollen und dort deinen Einfluss gelten machen. 

Viel Erfolg!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...