Generic Editor Array PropertyAttribute Tools

The Array handling in the editor is really poor.
I have been looking for a better way to use arrays adding button to insert delete or move elements.

Thanks to Naked Chicken who post a way to move string arrays using in this post : Tired of the limited viewing capabilities of strings in your inspector?

I’ve then tried to make a generic script that should work with almost any type of field. And the result is quite simple :

1595948--95981--$Example.png

It works with primitives and with Classes !!!

First adds a class ArrayAttribute.cs :

using UnityEngine;
using System.Collections;

public class ArrayAttribute : PropertyAttribute
{}

and a Property drawer inside an Editor folder

using UnityEngine;
using System.Collections;
using UnityEditor;

// The property drawer class should be placed in an editor script, inside a folder called Editor.
// Tell the ArrayDrawer that it is a drawer for properties with the ArrayAttribute.
[CustomPropertyDrawer(typeof(ArrayAttribute))]
public class ArrayDrawer : PropertyDrawer
{
	const float widthBt = 35;

	void addArrayTools(Rect position, SerializedProperty property)
	{
		string path = property.propertyPath;
		int arrayInd = path.LastIndexOf(".Array");
		bool bIsArray = arrayInd >= 0;

		if (bIsArray)
		{
			SerializedObject so = property.serializedObject;
			string arrayPath = path.Substring(0, arrayInd);
			SerializedProperty arrayProp = so.FindProperty(arrayPath);

			//Next we need to grab the index from the path string
			int indStart = path.IndexOf("[") + 1;
			int indEnd = path.IndexOf("]");

			string indString = path.Substring(indStart, indEnd - indStart);

			int myIndex = int.Parse(indString);
			Rect rcButton = position;
			rcButton.height = EditorGUIUtility.singleLineHeight;
			rcButton.x = position.xMax - widthBt * 4;
			rcButton.width = widthBt;

			bool lastEnabled = GUI.enabled;

			if (myIndex == 0)
				GUI.enabled = false;

			if (GUI.Button(rcButton, "Up"))
			{
				arrayProp.MoveArrayElement(myIndex, myIndex - 1);
				so.ApplyModifiedProperties();
				
			}

			rcButton.x += widthBt;
			GUI.enabled = lastEnabled;
			if (myIndex >= arrayProp.arraySize - 1)
				GUI.enabled = false;

			if (GUI.Button(rcButton, "Dn"))
			{
				arrayProp.MoveArrayElement(myIndex, myIndex + 1);
				so.ApplyModifiedProperties();
			}

			GUI.enabled = lastEnabled;

			rcButton.x += widthBt;
			if (GUI.Button(rcButton, "Del"))
			{
				arrayProp.DeleteArrayElementAtIndex(myIndex);
				so.ApplyModifiedProperties();
			}

			rcButton.x += widthBt;
			if (GUI.Button(rcButton, "Ins"))
			{
				arrayProp.InsertArrayElementAtIndex(myIndex);
				so.ApplyModifiedProperties();
			}
		}
	}

	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
	{
		addArrayTools(position, property);
		Rect rc = position;
		if (!property.isExpanded)
			rc.width -= widthBt * 4;

		EditorGUI.PropertyField(rc, property, label, true);
	}

	public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
	{
		return EditorGUI.GetPropertyHeight(property);
	}
}

Then in your own MonoBehavior adds a attribute class [Array]

example :

	[Array]
	public MyClass[] theArray;

	[Array]
	public int[] theIntArray;

I’ve added a unity package containing the 2 main classes and an example

I hope It could help.

1595948–95982–$ArrayPropertyAttribute.unitypackage (2.96 KB)

3 Likes

Then you can replace button with some icons; Imgur: The magic of the Internet

This works brilliantly for a Csharp code base, unfortunately my code base is in JS, I tried moving all of your code into an editor folder, however my code still complains of Array is not a valid attribute.
Of course I use @ as needed buy Javascript.
If you have any idea how to get this working in JS for custom Js classes I would be extremely great full

Thanks.

Hi Got this working, without the Array attribute, ie. Making a dedicated draw, for a specific class. It works great for this one off scenario. If you could shed some light on the other issue would be great.

Google has spit this as a result for my search, but the solution depicted here does not work anymore. Just leaving this here, so that whoever stumbles upon this in next 10 years wont waste time trying to implement this.