I can’t find a way to access the contents of an array which is a SerializedProperty.
Specifically:
// .... in MyScript ... //
public var myIntArray : int[] = [1, 2, 3, 4];
// ... in another script ... ///
var so : SerializedObject = new SerializedObject(MyScript);
var sp : SerializedProperty = so.FindProperty("myIntArray"); // serialized property of an int array
// In an effort to figure out how this works, I tried:
Debug.Log(sp.propertyType.ToString()); // value = Generic
Debug.Log(sp.hasChildren.ToString()); // value = True
Debug.Log(sp.hasVisibleChildren.ToString()); // value = True
Debug.Log(sp.type.ToString(); // value = vector ????
The returned SerializedPropertyType “Generic” is not a listed type in SeralizedPropertyType. The returned value for sp.type “vector” (not vector2 or vector3) is also confusing.
I can’t figure out how to access the values in the array from the SerializedProperty. Am I understanding this incorrectly? Do I have to so.FindProperty(myIntArray[#]) for each element in the array separately? If so, why is it not giving me an error when I do it for the whole array?
Edit: Forgot to ask, hasChildren and hasVisibleChildren are true. Not even sure what this is referring to. Docs for these has no information at all. How do I access these children?
Okay, so even if I try to get the size or elements of my array as SerializedProperty like this it fails (crashes)
sp = so.FindProperty("myIntArray.length");
sp = so.FindProperty("myIntArray[0]");
// Any attempt to access data in sp after this crashes unity
Debug.Log(sp.intValue); // CRASH
They specifically mention arrays being serializable more than one time on that page. Or course this is the case with public arrays in the inspector – the data gets saved, and one can override array values in the inspector for prefabs. However there appears to be no way of accessing arrays through SerializedProperty even though the inspector clearly does this when editing arrays.
Useless info:
I’m working on a prefab property editor which will let me change certain values in hundreds of prefabs (the master prefabs, not the instances in the scene) via a spreadsheed like view. If I just simply edit the values directly in the prefabs, all instances of those prefabs in the scene will not inherit the new changes. However, if I change the values through SerializedProperty, all instanced prefabs in the world automatically update values that haven’t been previously overridden which is the desired effect. However, not being able to edit arrays like this pretty much kills the effectiveness of this tool as I rely on arrays quite heavily.
It’s a matter of first getting the serialized property and then iterating through all its elements. I’ll post my final update when I get it all sorted.
BTW, I know of EditorGUI.PropertyField(), but I’m trying to access the array elements individually and not with the drop down GUI tool.
Okay, I found a way. Not the nicest, but whatever:
private var propertyName : String = "propertyName";
private var so : SerializedObject = new SerializedObject(target);
function OnGUI() {
// Since OnGUI runs continually, I need to "rewind" the index in the SerializedProperty each time back to the named property's index
var sp : SerializedProperty = so.FindProperty(propertyName); // Get SP again each time to "rewind" it to proper starting index. If you don't, Next() will continue off from the last position
var arrayLength : int = 0;
var count : int = 0;
var arrayElementCount : int = 0;
while(true) {
// Iteration 0 1 do not contain the data we need. This was verified by Debug.Log(sp.propertyType) through every iteration to find where the array length and starting indices were stored.
if(count == 2) { // Iteration 2: This should be the size of the array.
arrayLength = sp.intValue; // get array size
} else if(count > 2) { // Iteration 2+: The remaining should be the array elements
// Do something wonderful with the array here... in my case, create a GUI.IntField
arrayElementCount++; // Increment array element count so we know when to stop. sp.CountRemaining() will not help us because it gives the count of all remaining properties in the entire SO, not the children of this SP.
if(arrayElementCount == arrayLength) // we got the last array element
break; // stop iterating, we're done
}
sp.Next(true); // Step to the next property in the SerializedObject including children
count++;
}
}
Well anyway, it’s messy and could be cleaned up. It’s kinda slow too having to do all this iteration with Next(), but whatever.
For anyone else looking for a solution.
I made a small adjustment as you could only increase the size of the array or list.
The code below will increase and decrease the size.
All I changed was the for loop and some text display.
void ArrayGUI(SerializedObject obj, string name)
{
int size = obj.FindProperty(name + ".Array.size").intValue;
int newSize = EditorGUILayout.IntField(name + " Size", size);
if (newSize != size)
obj.FindProperty(name + ".Array.size").intValue = newSize;
EditorGUI.indentLevel = 3;
for (int i=0;i<newSize;i++)
{
var prop = obj.FindProperty(string.Format("{0}.Array.data[{1}]", name, i));
EditorGUILayout.PropertyField(prop);
}
}
public void InsertArrayElementAtIndex( int index );
public bool MoveArrayElement( int srcIndex, int dstIndex );
public SerializedProperty GetArrayElementAtIndex( int index );
You can set the value in the array index by writing to the SerializedProperty returned by GetArrayElementAtIndex.
Sorry for the noob question but the way Guavaman wrote it in the GUI function I can understand as it would display it in the editor but you guys wrote your code in a separate function “ArrayGUI” How would you implement it so that it would show the array in the editor GUI?
Seems this method now results in an element “N” fold out being displayed in the inspector (see screen shot). I’m using it to display a level editor for an array of levels one level at a time
maybe unity have changed how arrays are serialised? seems odd and is a bit annoying. You can remove the Element label using GUIConent.none but not the foldout icon.
private static void DrawPropertyArray(SerializedProperty property, ref bool fold) {
fold = EditorGUILayout.Foldout(fold, new GUIContent(
property.displayName,
"These are the waypoints that will be used for the moving object's path."), true);
if (!fold) return;
var arraySizeProp = property.FindPropertyRelative("Array.size");
EditorGUILayout.PropertyField(arraySizeProp);
EditorGUI.indentLevel++;
for (var i = 0; i < arraySizeProp.intValue; i++) {
EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(i));
}
EditorGUI.indentLevel--;
}
I added a place for a tooltip (You could put property.tooltip there too if you want), I added true to the end of the fold parameter so clicking the array triangle also folds and unfolds the array, I used var where possible, I reduced nesting by inverting the fold if statement, and I made the function static.