I’m bashing my head on custom inspector array handling.
I have 4 arrays that I want to all be the same size. I figured it would be better to have a custom inspector that handled making sure they are all the same size for me. I have a property which is the max size. It can be between 1 and 9 (inclusive). I put in an increment button and decrement button. For the increment button I call InsertArrayElementAtIndex for each of the array; likewise on decrement button I call DeleteArrayElementAtIndex.
This works great if the arrays are already populated. If the arrays aren’t then no elements are added.
What I want to do is initialize these arrays to a size of 1 in the inspector. I can not figure out, however, how to assign a new array to these SerializedProperties.
There has to be a way to do it, since the default inspector will create/initialize an array when you increase the size. But I am unable to figure out how to do so.
Argh, turns out it is dead simple. If I replace “InsertArrayElementAtIndex” with “arraySize += 1” and “DeleteArrayElementAtIndex” with “arraySize -= 1” then everything works great.
In fact, I discovered it myself, and just now I’ve read your solution - after banging my head for hours.
I needed to add elements to an array, and naive as I am, I thought that InsertArrayElementAtIndex(int index) would be responsible for creation of new (empty) slots. I was getting:
Retrieving array element that was out of bounds
UnityEditor.SerializedProperty:InsertArrayElementAtIndex(Int32)
The thing is that SerializedProperty array isn’t documented as it should be.Also have overlooked the arraySize property, since I thought it is read only. If fact, it is also the setter.
So, to help the next victim of Unity’s serialized array, here are the operations available for manipulating it:
// ADDS the element to the end of the array
YourSerializedProperty.arraySize ++;
// REMOVES the element from the end of the array
YourSerializedProperty.arraySize --;
// INSERTS the element to the specified array index.
// Note: The index has to be prepopulated, meaning this cannot be used to push to the end of the array.
YourSerializedProperty.InsertArrayElementAtIndex(int index);
// REMOVES the element from the specified array index, but not the last one.
YourSerializedProperty.DeleteArrayElementAtIndex(int index);
// SETS the value at the specified slot
YourSerializedProperty.GetArrayElementAtIndex(index).objectReferenceValue = value;
// GETS the value at the specified slot
return YourSerializedProperty.GetArrayElementAtIndex(index).objectReferenceValue;
I have a custom inspector script which manipulates a SerializedProperty array. What I noticed is that when I use DeleteArrayElementAtIndex it doesn’t actually delete an item but leaves a null at a specified index.
I added a second check which is triggered during the next Layout event which looks for null values and does the same DeleteArrayElementAtIndex on the first one. And this time array actually shrinks.
int gestureIndexToRemove = -1;
for (int i = 0; i < serializedGestures.arraySize; i++) {
if (serializedGestures.GetArrayElementAtIndex(i).objectReferenceValue == null || GUILayout.Button("remove")) gestureIndexToRemove = i;
}
if (gestureIndexToRemove > -1) removeGestureAt(gestureIndexToRemove);
So when I press “remove” button it goes to removeGestureAt but right in the next Layout event it encounters a null at this index and goes to removeGestureAt again. After the second call the element is finally removed.
I tried all the possible combinations of serializedObject.ApplyModifiedProperties() and EditorUtility.SetDirty(target) inside removeGestureAt function but nothing helped…
I know the thread is old, But I’m having the same problem, how does one do this:
OnGUI >> ButtonPress >> DeleteArrayAtElement
when I do it, it removes two values not one, obviously because the OnGUI draws two-times per frame, I’m using EditorWindow so it needs to be done inside the OnGUI
The latest victim of this poorly-documented class also thanks you for posting this. I beat my head against the wall for two hours before finally seeing the solution here.
Hi I know this is an old post but I think I am having the same problem. I have made an Inventory editor that works with 4 items, as soon as I add even just one more the 5th one does not work. The error points me to a serialized array in my code.
Inventory Editor code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor (typeof(Inventory))]
public class InventoryEditor : Editor {
private SerializedProperty itemImagesProperty;
private SerializedProperty itemsProperty;
private bool[] showItemSlots = new bool[Inventory.numItemSlots];
private const string inventoryPropItemImagesName = "itemImages";
private const string inventoryPropItemsName = "items";
private void OnEnable()
{
itemImagesProperty = serializedObject.FindProperty(inventoryPropItemImagesName);
itemsProperty = serializedObject.FindProperty(inventoryPropItemsName);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
for (int i= 0; i < Inventory.numItemSlots; i++)
{
ItemSlotGUI(i);
}
serializedObject.ApplyModifiedProperties();
}
private void ItemSlotGUI (int index)
{
EditorGUILayout.BeginVertical(GUI.skin.box);
EditorGUI.indentLevel++;
showItemSlots[index] = EditorGUILayout.Foldout(showItemSlots[index], "Item slot " + index);
if (showItemSlots[index])
{
EditorGUILayout.PropertyField(itemImagesProperty.GetArrayElementAtIndex(index));
EditorGUILayout.PropertyField(itemsProperty.GetArrayElementAtIndex(index));
}
EditorGUI.indentLevel--;
EditorGUILayout.EndVertical();
}
}
Inventory code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Inventory : MonoBehaviour
{
public Image[] itemImages = new Image[numItemSlots];
public Item[] items = new Item[numItemSlots];
public const int numItemSlots = 5;
public void AddItem(Item itemToAdd)
{
int id = itemToAdd.id;
if (items.Length <= id)
return;
if (items [id]== null)
{
items[id] = itemToAdd;
itemImages[id].sprite = itemToAdd.sprite;
itemImages[id].enabled = true;
return;
}
}
public void RemoveItem(Item itemToRemove)
{
for (int i = 0; i < items.Length; i++)
{
if (items[i] == itemToRemove)
{
items[i] = null;
itemImages[i].sprite = null;
itemImages[i].enabled = false;
return;
}
}
}
}
If you originally created your component when Inventory.numItemSlots was 4, then its itemImages[ ] and images[ ] arrays were created with 4 elements in that instance. Once Unity creates a component on a GameObject, it doesn’t resize them if you change the array size in your class. You can force Unity to reset them to their initializer values by selecting Reset from the gear menu. In your code, you can force the arrays to be the correct size by adding these lines:
public override void OnInspectorGUI()
{
serializedObject.Update();
itemImagesProperty.arraySize = Inventory.numItemSlots;
itemsProperty.arraySize = Inventory.numItemSlots;
for (int i= 0; i < Inventory.numItemSlots; i++)
...