Dfctps
Dfctps

Reputation: 115

Serialization of a list of custom objects in unity

While trying to make a script for building assets, I ran into an issue with unity's serialization. I have a class in which I store some arbitrary information, which is then stored in an array in a MonoBehaviour on a prefab. I cannot for the life of me get the array to save however, as when I make the object into a prefab it loses the list's values. I have tried using [System.Serializable] and ScriptableObject, but both seem to pose their own new issues.

For instance, using ScriptableObject would mean having to save the data objects as assets, which would become way too much since these objects can get to hundreds in number.

Am I making a mistake in my understanding of unity's serialization? Is there a way to get this working without the ScriptableObject approach of saving every ArbitraryInfo object in an asset?

Data object:

[System.Serializable]
public class ArbitraryInfo{
    public int intValue;
    public Vector3 vectorValue;
}

OR

public class ArbitraryInfo : ScriptableObject {
    public int intValue;
    public Vector3 vectorValue;

    void OnEnable() {
        hideflags = HideFlags.HideAndDontSave;
    }
}

Behaviour:

public class MyBuilder : MonoBehaviour {
    public ArbitraryInfo[] infoArray;
}

Editor:

[CustomEditor(typeof(MyBuilder))]
public class MyBuilderEditor : Editor {
    private SerializedProperty infoArrayProperty;

    void OnLoad() {
        infoArrayProperty = serializedObject.FindProperty("infoArray");
    }

    void OnInspectorGUI() {
        serializedObject.Update();

        for (var i = 0; i < infoArrayProperty.arraySize; i++) {
            if (i > 0) EditorGUILayout.Space();

            var info = infoArrayProperty.GetArrayElementAtIndex(i).objectReferenceValue as ArbitraryInfo;

            EditorGUILayout.LabelField("Info " + i, EditorStyles.boldLabel);

            info.intValue = EditorGUILayout.IntField(info.intValue);
            info.vectorValue = EditorGUILayout.Vector3Field(info.vectorValue);
        }

        serializedObject.ApplyModifiedProperties();
    }
}

EDIT 1, Thank you derHugo

I changed my code to incorporate the changes. Now there are errors for ArbitraryInfo not being a supported pptr value.

Secondly, ArbitraryInfo no longer being a ScriptableObject poses the question of how to initialize it. An empty object can be added to infoArrayProperty through infoArrayProperty.arraySize++, but this new empty object seems to be null in my case. This might be due to the pptr issue mentioned above.

EDIT 2

The issue I was having was caused by another piece of code where I tried to check if infoArrayProperty.objectReferenceValue == null. I changed this to another check that did the same thing and everything worked!

Upvotes: 1

Views: 8941

Answers (1)

derHugo
derHugo

Reputation: 90862

No, no ScriptableObject needed.

But note that GetArrayElementAtIndex(i) returns a SerializedProperty. You can not simply parse it to your target class.

so instead of

var info = infoArrayProperty.GetArrayElementAtIndex(i).objectReferenceValue as ArbitraryInfo;

and

info.intValue = EditorGUILayout.IntField(info.intValue);
info.vectorValue = EditorGUILayout.Vector3Field(info.vectorValue);

you have to get the info's SerializedPropertys by using FindPropertyRelative:

var info = infoArrayProperty.GetArrayElementAtIndex(i);

var intValue = info.FindPropertyRelative("intValue");
var vectorValue = info.FindPropertyRelative("vectorValue");

than you can/should use PropertyFields

EditorGUILayout.PropertyField(intValue);
EditorGUILayout.PropertyField(vectorValue);

allways try to avoid using direct setters and use those SerializedProperties instead! This provides you with Undo/Redo functionality and marking the changed Behaviour/Scene as unsaved automatically. Otherwise you would have to tak care of that manually (... don't ^^).

Upvotes: 2

Related Questions