- Home /
Insert new custom class element with _default_ values to a SerializedProperty array?
In a custom inspector, I am adding a new custom class "DSReaction" element to a native array named "reactions" obtained via a SerializedProperty. To add the new element to the "reactions" array, I'm using the "InsertArrayElementAtIndex" function.
Here is a stripped-down version of the code:
using UnityEditor;
[CustomEditor(typeof(DSReacter))]
public class DSReacterEditor : Editor
{
private DSReacter T;
private SerializedObject Reactor;
private SerializedProperty reactions;
private void OnEnable()
{
T = target as DSReacter;
Reactor = new SerializedObject(T);
reactions = Reactor.FindProperty("reactions");
}
public override void OnInspectorGUI()
{
Reactor.UpdateIfDirtyOrScript();
if (GUILayout.Button("New Reaction"))
reactions.InsertArrayElementAtIndex(0);
Reactor.ApplyModifiedProperties();
}
}
Unfortunately, if the array is not empty, this automatically creates the new element using the variable valuables of the preceding element.
How can I add a new element, but have it retain the default class variable values, as opposed to the values of the preceding element?
Thank you in advance, and be well!
- S.
Answer by Bunny83 · Jun 25, 2016 at 09:32 PM
That's not possible using SerializedObject / Property. "InsertArrayElementAtIndex" works the same way as the default inspector. When you change the "size" of an array the new values at the end are initialized with the last element in the array. The serialization system doesn't seem to offer a way to initialize an object instance with default values.
The only alternative would be to access the target object manually and modify the array yourself.
Since you already use the target property you should be able to do the following. Note since you did not provide your "DSReacter" class nor your custom data class i can only assume that the "reactions" variable is a native array:
if (GUILayout.Button("New Reaction"))
{
Undo.RecordObject(T, "Added Reaction");
var list = new List<YourCustomClass>(T.reactions);
list.Add(new YourCustomClass());
T.reactions = list.ToArray();
}
Note the Undo.RecordObject is required or the editor won't detect the changes.
@Bunny83: Yes, you assumed correctly that T.reactions is a native array.
I had presumed that I was overlooking a method offered by SerializedProperty. Glad to know that it was the lack of a method as opposed to my inability to locate one.
Thank you for your response and example. I've tested it, and it works perfectly.
Additionally, I've updated my original post to clarify it for future reads based on your comment.
Thank you again, and be well!
- S.
Yes, the SerializedProperty is lacking some important features. This is due to the fact how the serialization system is implemented. It actually doesn't care about class instances as it's only build on top of the actual data that get saved. The fields of a custom class get serialized in a "flattend array fashion". So from the serialization system's point of view only the serialized properties exist. If you have two or more nested custom classes it's a pain to modify or access any of those custom class values through a SerializedObject / Property. It's not possible to get access to an actual instance of a custom class through the serialization system. You would have to go through the actual target / targets reference.
@Bunny83: That makes perfect sense. I'm familiar enough with accessing properties via target and manually registering them in Undo. SerializedProperty's features are far newer to me (though I've been using them for a while). $$anonymous$$y primary concern is the need to make variables accessible through target public, therefor di$$anonymous$$ishing my efforts for making them immutable.
Wouldn't it be possible to do something like
reactions.InsertArrayElementAtIndex(0);
reactions.GetArrayElementAtIndex(0).objectReferenceValue = new Reaction();
ins$$anonymous$$d?
@Plattinator: Good question!
That's was actually the avenue I was exploring, but continuously found myself at a dead end.
Using variants of your code, I receive the error: "Cannot convert source type 'DSReaction' to target type 'UnityEngine.Object'."
No, that's not possible because the "objectReferenceValue" only works for references between assets. The type of objectReferenceValue is not System.Object but UnityEngine.Object. So you can only handle references to other serialized objects with that. Things like references to GameObjects or other $$anonymous$$onoBehaviours