How to add elements to a SerializedProperty that refernces a List < T > where T : is a Serializable 'regular' class?


I have a SerializedProperty that is referencing a List, where Decision is just a regular class (Not a MonoBehaviour, nor a ScriptableObject) mocked with [System.Serializable]

The problem I’m facing, I can’t add new decisions!

Well, the way one would add something to a list that’s referenced via a SerializedProperty is to manually increase the size of the array (via arraySize), and then set the last element directly, a method to do that would be (for strings):

public static void Add(this SerializedProperty prop, string value)
	prop.GetAt(prop.arraySize - 1).stringValue = value;

The problem is that I can’t do this to add my Decision object:

	prop.GetAt(prop.arraySize - 1).objectReferenceValue = value;

Because, objectReferenceValue is a UnityEngine.Object, while Decision is a System.Object
There’s no way to convert from the first to the second.

There are obvious ways around this, one to make the Decision class a ScriptableObject that way it is a UnityEngine.Object, other is to just get a direct reference to the list and just add the element - but then I would have to register undo manually.

I would really love to just keep my class a normal class, and be able to add it to the list via the SerializedProperty

Any ideas?


Found the solution! - I was just watching this video for the 3rd time maybe, when I noticed @29:40, he’s just incrementing the arraySize to add a new entry. But then I said oh well, that’s because the entry is probably a ScriptableObject, but… surprise surprise @30:19 - it’s a normal class!

Couple of things to take note of:

  1. Doing a size increment like
    that will duplicate the last element
    in the array/list.
  2. If after the increment you wanted to ‘directly’ access the element you added, you have to do a serializedObject.ApplyModifiedProperties(); so that the change will get reflected on the actual target object.
  3. Not a horrible thing to notice, but you should put the initialization logic of your object (in my case Decision) in its constructor since you can’t really get a reference to it via the SerializedProperty to call some Init method (if there’s a method you’d want to call, you have to call it directly like (target as MyClass).Field.Method(); )

So yeah that’s about it, arraySize++