Editor Data removes on Awake

I know this question and answers to it is all over the place, but in that case, it’s al little different.

All of my objects are serializable, everything is properly serialized.

The object that I am trying to serialize works perfectly until the Awake at which it resets.
It’s a list containing a serializable, non-generic, tree-structured class.

I tried setting the target as dirty as well as changing the scene to dirty but it just doesn’t work… the target is a reader that takes that list and reads data from it. The reading code works perfectly well (as it was tested before without the custom serialization)

The only thing that is not working, as usual, is that the data from the list is removed on Awake, the only thing that’s left is a new empty list. Which makes me suspect because I assumed it’d be null…

Any ideas?

Hi ! Bit hard to tell without knowing anything about your code, maybe you can post your class here ?

First things i’d checked ; Are you using a Custom inspector ? Are you implementing ISerialization Interface ? Did you tag [System.Serializable] on your objects’ class ? Does the tree structure has more than 7 depth or does objects references both ways ?

Are you using a custom inspector? Yes.
Are you implementing ISerialization Interface? No, I’ve never used it but I’ll check.
Did you tag [System.Serializable] on your objects’ class? Yes.
Does the tree structure has more than 7 depth or do objects reference both ways? Specifically with my examples no, but it could get there, and wdym reference both ways?

Information about objects:

Route -
string name
string displayName
List<KeyValuePair<Character, string>> lines
List<KeyValuePair<Choice, int>> choices

Choice -
Route origin
Route[ ] subRoutes

Character -
string name
string hexColor
Sprite image

The way it is made in a tree-structured way is that a route has a list of choices that each have a list of routes and so on.

If you need more information about the code let me know

Not sure at all what you are doing, but these ones are not going to be serializable by Unity, so they won’t show in the Inspector either:

List<KeyValuePair<Character, string>> lines
List<KeyValuePair<Choice, int>> choices

The way I serialized them is:

for Lines:
making a toggle foldout (to resemble a list)
containing two textFields and a label
textField one for character name, label for a " : " (separation, meant just for style), and another textField for the string which is the line, it does store the data

for Choices:
making a toggle foldout (to resemble a list)
using a helpBox verticle
making a function to generate a Route, made out of simple fields, and doing the same for Choice, as Choice is basically a list of Routes.

Edit:
the data is being stored perfectly because when I run methods on them before awaking, it worls
I just don’t know how to pass Unity’s serilizing

And more than that, even if those two lists will not be serialized, it doesn’t explain why NONE of the information was serialized, it should have keep a list of the Routes, with their names, display names, and only removing those two lists from each. Yet, it removed EVERYTHING

I think this is a pretty fine look at what I got

first image is when all lists are closed
second is seeing the list of lines
third is list of choices (you can see one and a half choices… the second one couldn’t get into the frame fully)

Edit
I already made a working version supporting text files(last image)

the things is just silencing my exception system so ignore it… lol ignore the ignore

There are more stuff to it, like in-text commands, a wide exception system, character customization etc… but I just showed the very basic that actually relates to the problem(The 4th one doesn’t have to do with the problem I just showed it so you can get a picture of what that graph in the inspector creates)


6402424--714295--Lines.PNG

The way you serialize it? I think you’re confusing some things here. A custom inspector has no (absolute zero) influence on how and if the data is serialized. A custom inspector is only responsible for how the data is presented. Like @eses said your data structures are not supported by Unity’s serialization system. Also your classes have circular references which is generally not supported, no matter if you use them or not. You can not store hierarchical data in custom serializable classes this way. A common solution is to “unroll” your hierarchy into a flat array and reconstruct the actual hierarchy from IDs / indices of that array.

Whoops-
yeah, Ik it only affects the data presentation, but I was wrong, my bad.
umm… yeah when I said tree-structured I meant hierarchical. I’ll try to unroll it (which means I’ll need to ID them… I’ll look into it, thanks for your reply)

You can actually use the ISerializationCallbackReceiver to kind of implement an auto unroll feature. I’ve quickly hacked this example together based on the bits we know about your case:

Example

    [System.Serializable]
    public class Data : ISerializationCallbackReceiver
    {
        [System.NonSerialized]
        public List<Route> routes;
        [SerializeField]
        private List<int> m_RouteIDs;
        [SerializeField]
        private List<Route> m_AllRoutes;

        public void OnAfterDeserialize()
        {
            if (routes == null)
                routes = new List<Route>();
            foreach (var route in m_AllRoutes)
            {
                foreach(var choice in route.choices)
                {
                    choice.origin = m_AllRoutes[choice.m_OriginID];
                    if (choice.subRoutes == null)
                        choice.subRoutes = new List<Route>();
                    choice.subRoutes.Clear();
                    foreach (int routeID in choice.m_SubRouteIDs)
                        choice.subRoutes.Add(m_AllRoutes[routeID]);
                }
            }
            foreach (var routeID in m_RouteIDs)
                routes.Add(m_AllRoutes[routeID]);
        }

        public void OnBeforeSerialize()
        {
            if (m_AllRoutes == null || m_RouteIDs == null)
            {
                m_AllRoutes = new List<Route>();
                m_RouteIDs = new List<int>();
            }
            m_AllRoutes.Clear();
            m_RouteIDs.Clear();
            foreach (var route in routes)
            {
                AddRoute(route);
                m_RouteIDs.Add(route.m_ID);
            }
        }
        private void AddRoute(Route aRoute)
        {
            aRoute.m_ID = m_AllRoutes.Count;
            m_AllRoutes.Add(aRoute);
            foreach(var choice in aRoute.choices)
            {
                choice.m_OriginID = aRoute.m_ID;
                if (choice.m_SubRouteIDs == null)
                    choice.m_SubRouteIDs = new List<int>();
                choice.m_SubRouteIDs.Clear();
                foreach (var route in choice.subRoutes)
                {
                    AddRoute(route);
                    choice.m_SubRouteIDs.Add(route.m_ID);
                } 
            }
        }
    }

    [System.Serializable]
    public class Route
    {
        public string name;
        public string displayName;
        public List<Line> lines;
        public List<Choice> choices;
        [System.NonSerialized]
        public int m_ID;
    }

    [System.Serializable]
    public class Line
    {
        public Character character;
        public string text;
    }
    [System.Serializable]
    public class Choice
    {
        public int ID;
        [System.NonSerialized]
        public Route origin;
        [System.NonSerialized]
        public List<Route> subRoutes;

        public int m_OriginID;
        public List<int> m_SubRouteIDs;
    }
    [System.Serializable]
    public class Character
    {
        public string name;
        public string hexColor;
        public Sprite image;
    }

Note that I haven’t tested the code. Also note that in order to work with those classes you don’t want to use Unity’s SerializedObject / SerializedProperty in your custom inspector but instead use the old “target” reference. The “Data” container will essentially take care of everything. You just create your Route, Choice, Line, Character objects just as you like and don’t touch any of the m_ fields manually. The serialization callback interface will take care of converting your hierarchical structure into a flat array and assigns the indices as IDs. It also automatically reconstructs your hierarchy after deserialization.

Note that this is just meant as an example. Also note that if you want to reference the same “Character” in multiple Lines, you may want to use IDs for those as well because after deserialization your character objects would be duplicated.

For more information see Script Serialization.

Hiho, yeah @Bunny83 solution is the way to go. Just be careful that OnBeforeSerialize & OnAfterSerialize are usually called … A LOT when inspector is opened (especially OnBeforeSerialize). So if you don’t want your inspector to be a burden, try adding some basic checks that will allow minimum calculations in case your inspector lists are not updated.

Also since you have a custom editor, the above should in theory not be necessary, though you have to keep in mind (you probably already know, just in case) :
1°) In a custom inspector, you should not assign anything directly to a variable, but to its serialized representation
For example

private MyObjectEditorClass m_Object;
private List<CustomClass> m_CustomList;
SerializedProperty m_CustomListProperty;
SerializedObject GetTarget;

void OnEnable()
{
    m_Object = (MyObject)target;
    GetTarget = new SerializedObject(m_Object);

    m_CustomList = m_Object.skillsTree;
    m_CustomListProperty = GetTarget.FindProperty("CustomListName"); // This is what you'll be modifying in the inspector
}
// Example
public override void OnInspectorGUI()
{
    // Initialize
    GetTarget.Update();

    base.OnInspectorGUI();
   
    // Custom Display
    // Show(m_CustomListProperty);
    // Adding List Element
    // m_CustomListProperty.InsertArrayElementAtIndex(m_CustomListProperty.arraySize);
   
    GetTarget.ApplyModifiedProperties();
}
  1. Since keyvaluepairs are not serialized by default, you have to define your own serialization (or find some custom package) to be able to use those FindProperty etc… (task is basically defining your own KeyValuePair
    as an XML serializable collection of keys and values, and implement methods to read/write that XML).
    Note that there is a reason why KeyValuePair is not serialized in the first place (forgot but you’ll prob find it easily if you need to).

Final note ; I’d advice you not to work with keyValue pairs. If you need a serialized dictionnary, use a custom one you can find pretty much anywhere (i’m using OdinInspector for so much more reasons), else i’d suggest you to keep working with lists of custom class, you can do pretty much anything with those and if you don’t intend to put hundreds/thousands of entries there is no performance drop between accessing a List compared to a dictionnary.