Jump to content
Unity Insider Forum

Serialize Frage


MustafGames

Recommended Posts

    public List<Variable> base_var = new List<Variable>();

    [NonSerialized]
    public List<Variable> var = new List<Variable>();

    public void OnBeforeSerialize () {
    }

    public void OnAfterDeserialize () {
        var = base_var;
    }

Grüße,

ich habe diesen Scriptausschnitt gefunden, nur bräuchte ich eine Erklärung dazu.

"base_var" zeigt es im Inspector an und es wird auch Serialized wenn das Spiel läuft, wie ist es mit "var".

 

Ich möchte es so das ich im Editor Inspector "base_var" definiere und während des Runtime (Spiel läuft) soll "var" alle Werte von "base_var" erhalten benutzt werden.

 

MfG Mustaf

Link zu diesem Kommentar
Auf anderen Seiten teilen

OnAfterDeserialize wird (wenn die Klasse auch das Interface ISerializationCallbackReceiver implementiert) direkt nach dem Deserialisieren aufgerufen, also dem Schritt, in dem Unity die ganzen Sachen lädt, die im Editor eingestellt wurden. Der Ansatz ist daher nicht schlecht, aber er wird hier vergessen, dass List<T> ein Objekt ist und kein Struct, also Referenzsemantik hat. Die letzte Zeile kopiert die Referenz auf die geladene Liste in die zweite Variable, und beide Variablen zeigen danach auf dieselbe Liste (und nicht auf die gleiche). Wenn du dann an dieser Liste etwas änderst (und dabei ist es egal über welche Variable), dann gelten diese Änderungen auch für die serialisierte Variable.

Bei Komponenten ist das egal, weil die sowieso zurückgesetzt werden. Ich nehme aber mal an, hier geht's um ScriptableObjects. Wenn du da verhindern willst, dass Laufzeit-Änderungen persistent sind, dann musst du eine Kopie der Liste erstellen:

var = new List<Variable>(base_var);

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke genau das war, was noch gefällt hat.

Also für andere nochmal zur Erklärung:

public List<Transform> listA = new List<Transform>();
public List<Transform> listB = new List<Transform>();
  
Start () {
  listB = listA //Beide Listen greifen auf die selbe Liste zurück, welche beim starten des Runtimes erstellt wird.
  listB = new List<Transform>(listA); //Es wird eine komplett neue Liste erstellt als Kopie von listA, jetzt sind beide Listen unterschiedlich bearbeitbar.
}

Problem trotz der neuen Liste, ändert sich die Liste welche im ScriptableObject ist:

public class VariableObject : ScriptableObject, ISerializationCallbackReceiver {

    public List<Variable> base_var = new List<Variable>();

    [NonSerialized]
    public List<Variable> var = new List<Variable>();

    public void OnBeforeSerialize () {
    }

    public void OnAfterDeserialize () {
        var = new List<Variable>(base_var);
    }

    public Variable FindByArgument (string arg) {
        foreach (Variable v in var) {
            if (v.argument == arg) {
                return v;
            }
        }
        return null;
    }
}

Mit "FindByArgument" greife ich auf einzelne Objekte zu und dann ändere ich diese, dann wird aber die Variable im ScriptableObject geändert und nicht die im Runtime temporär generierte.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn "Variable" eine Klasse ist, dann hast du hier wieder dasselbe Problem. Mit

new List<T>(originalList)

erstellst du eine so genannte "Flat Copy". Du hast also eine neue Liste, die aber wieder dieselben Objekte referenziert. Du kannst jetzt überlegen, ob du "Variable" zu einem Struct machst (dann ist die Referenzsemantik weg) oder du baust eine Deep Copy, wobei nicht nur die Liste Kopiert wird, sondern auch die Objekte, die sie referenziert.

Dazu solltest du noch deine base_var private machen, damit da kein Script dran rumpfuschen kann. Mit einem [SerializeField] darüber wird das Ding trotzdem im Editor angezeigt und serialisiert.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ok danke.

Variable sieht so ungefähr aus:

[Serializable]
public class Variable {
}

Ich muss diese als Class nehmen da es sonst nicht funktionieren würde was diese macht.

Wie erstellt man eine Deep Copy?

 

PS: Andere Frage die mich hier auch etwas weiterbringen würde, gibt es ein Feld welches im Inspector angezeigt wird wo man ein (int, Vector3 etcc..) auswählen kann solange dieser public ist?

So wie das UnityEvent einen Component auswählen kann  und dann eine public void oder ähnliches auswählt, so möchte ich etwas wo man aus Komponenten eine Variable auswählen kann, gibt es sowas?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 23 Stunden schrieb MustafGames:

Ich muss diese als Class nehmen da es sonst nicht funktionieren würde was diese macht.

Was macht sie denn, was ein Struct nicht kann? 🤔

vor 23 Stunden schrieb MustafGames:

Wie erstellt man eine Deep Copy?

Kommt ein bisschen darauf an. Die simpelste Variante ist: Neue Liste erstellen, durch die alte Liste durchgehen und Kopien aller Objekte in die neue Liste einfügen. Dafür würde sich eine "Clone()"-Methode in deiner Klasse anbieten. Allerdings bist du dann im Zweifelsfall auch nur eine Ebene "deep". Objekte, die wiederum von deinen kopierten Objekten referenziert werden, werden nur dann kopiert, wenn du das explizit implementierst.

vor 23 Stunden schrieb MustafGames:

PS: Andere Frage die mich hier auch etwas weiterbringen würde, gibt es ein Feld welches im Inspector angezeigt wird wo man ein (int, Vector3 etcc..) auswählen kann solange dieser public ist?

Nicht wirklich. Und da C# typstark ist (und das ist auch gut so), willst du das auch nicht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Variable besitzt booleans, da Variable in diesem Fall noch einen CustomPropertyDrawer hat, das klappt auch alles super.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

[Serializable]
public class Variable {
    public enum VarType { String, Int, Float, Vector3, Object, Vector4 };
    public VarType type;

    public string argument;

    public bool isList = false;

    public string var_string;
    public int var_int;
    public float var_float;
    public Vector3 var_vec;
    public UnityEngine.Object var_obj;
    public Vector4 var_vec4;

    public List<string> list_string;
    public List<int> list_int;
    public List<float> list_float;
    public List<Vector3> list_vec;
    public List<UnityEngine.Object> list_obj;
    public List<Vector4> list_vec4;

    public void SetVariable (object o) {
        switch (type) {
            case VarType.String:
                if (isList) {
                    list_string = (List<string>)o;
                } else {
                    var_string = (string)o;
                }
                break;
            case VarType.Int:
                if (isList) {
                    list_int = (List<int>)o;
                } else {
                    var_int = (int)o;
                }
                break;
            case VarType.Float:
                if (isList) {
                    list_float = (List<float>) o;
                } else {
                    var_float = (float)o;
                }
                break;
            case VarType.Vector3:
                if (isList) {
                    list_vec = (List<Vector3>)o;
                } else {
                    var_vec = (Vector3)o;
                }
                break;
            case VarType.Object:
                if (isList) {
                    list_obj = (List<UnityEngine.Object>)o;
                } else {
                    var_obj = (UnityEngine.Object)o;
                }
                break;
            case VarType.Vector4:
                if (isList) {
                    list_vec4 = (List<Vector4>)o;
                } else {
                    var_vec4 = (Vector4)o;
                }
                break;
        }
    }

    public object GetVariable () {
        switch (type) {
            case VarType.String:
                if (isList) {
                    return list_string;
                } else {
                    return var_string;
                }
            case VarType.Int:
                if (isList) {
                    return list_int;
                } else {
                    return var_int;
                }
            case VarType.Float:
                if (isList) {
                    return list_float;
                } else {
                    return var_float;
                }
            case VarType.Vector3:
                if (isList) {
                    return list_vec;
                } else {
                    return var_vec;
                }
            case VarType.Object:
                if (isList) {
                    return list_obj;
                } else {
                    return var_obj;
                }
            case VarType.Vector4:
                if (isList) {
                    return list_vec4;
                } else {
                    return var_vec4;
                }
        }
        return null;
    }
}

 

Ok schade, den so könnte man ein universelles EventSystem bauen wo man auf das schreiben von Script nicht mehr ganz so angewiesen ist wenn man static Methoden hat und die richtigen Variablen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Weil ich brauch das genau so, den ich habe ein ScriptableObject welches verschiedenste Werte speichern kann, aber nur den ausgewählten Wert zurrückgibt und dazu den passenden CustomPropertyDrawer um es übersichtlich zu lassen. Problem ist nun nur noch das im Runtime die Werte temporär kopiert werden und nach beenden vom Spiel soll das ScriptableObject noch die originalen Werte haben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 3 Stunden schrieb MustafGames:

ich brauch das genau so

Ich wette, das stimmt nicht wirklich. Ein Objekt, das einen Wert eines beliebigen Typs speichern kann, ist bei typstarken Sprachen grundsätzlich etwas falsches. Was auch immer du eigentlich zu erreichen versuchst, es geht mit Sicherheit auch anders.

Darum einfach mal gefragt: Was willst du mit diesem ScriptableObject erreichen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Na das es beliebige Werte speichern kann ist nur die Bezeichnung, im script selber speichert es alle möglichen Werte (je nachdem was man vorgibt) und dann kann man sich einen Wert auswählen (für mich einfach als universelles ScriptableObject zum speichern von verschiedensten Werten in einem Objekt).

Wie gesagt brauche ich nur von dem Objekt eine Kopie (welche dann genutzt wird im Runtime) beim Start (laden oder aufrufen des Objekts), kann man das nicht irgendwie machen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum willst du unbedingt mit dem Kopf durch die Wand? Du stehst halt gerade vor einem Haufen Holz und willst einen Tisch bauen, und jetzt fragst du mich, wie du die Nägel mit der Hand reinschlagen kannst, anstatt dir von mir einen Hammer geben zu lassen. Vertrau mir bitte einfach, dass dein aktueller Weg dir mehr Problem als Lösung sein wird. Erzähl mir doch mal lieber, was das für ein Tisch werden soll.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Verstehe jetzt das Problem nicht ganz, meine Art und Weise mein Spiel zu entwickeln ist natürlich deutlich anders als bei allen anderen.

Hab es nun anders gelöst, diese Variante das im Runtime eine Kopie erstellt werden soll wäre zwar für mich nicht übel aber im Moment nutze ich diesen Script (ScriptableObject) zum speichern von z.b. Spieleinstellungen, Allgemeine Werte (Minimale Angelzeit und Maximale Angelzeit). 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...