← Back


Dirty Unity: Move array elements in the inspector

by Totally Not Alex
12. March 2019


Hey guys,

here comes an introduction to a small series: Dirty Unity, in which I will share small, but handy C# code snippets in the future. So this series is aimed at (who would have thought that?) Unity3D developers and those who want to become developers or just work with Unity for other reasons. This series is named “Dirty Unity” because the solutions aren’t the most elegant/performant and because they solve problems that should not exist in Unity in the first place.

Today’s topic is an editor script that adds context menu entries for array elements in the inspector that allow moving the element in the array. Right-clicking an element will pop up the following:

Crazy Twist: “Down” and “Up” means that the element moves one index up/down and therefore moves the other way round in the inspector view. Wow, so unintuitive!

using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace AlienCore
{
	public static class MoveArrayElementEditor
	{
		[InitializeOnLoadMethod]
		static void Start ()
		{
			// For more information visit: https://docs.unity3d.com/ScriptReference/EditorApplication-contextualPropertyMenu.html
			EditorApplication.contextualPropertyMenu += OnPropertyContextMenu;
		}

		static void OnPropertyContextMenu (GenericMenu menu, SerializedProperty property)
		{
			if (property == null) return;

			// Path of an array is "PropertyName.Array.data[arraylength]", so let's look if the property is an array
			string propertyPath = property.propertyPath;
			if (!propertyPath.Contains (".Array.data[") || !propertyPath.EndsWith ("]")) return;

			// Split the property path and find out the array length
			string[] fullPathSplit = propertyPath.Split ('.');
			string ending = fullPathSplit [fullPathSplit.Length - 1];
			int index = 0;
			if (!int.TryParse (ending.Replace ("data[", "").Replace ("]", ""), out index)) return;

			// Rebuild the path without the ".Array.data[arraylength]" stuff
			string pathToArray = string.Empty;
			for (int i = 0; i < fullPathSplit.Length - 2; i++)
			{
				if (i < fullPathSplit.Length - 3)
				{
					pathToArray = string.Concat (pathToArray, fullPathSplit [i], ".");
				}
				else
				{
					pathToArray = string.Concat (pathToArray, fullPathSplit [i]);
				}
			}

			// Get the serialized target object and the property of the array with the path
			Object targetObject = property.serializedObject.targetObject;
			SerializedObject serializedTargetObject = new SerializedObject (targetObject);

			SerializedProperty serializedArray = serializedTargetObject.FindProperty (pathToArray);
			int arrayLength = serializedArray.arraySize;

			if (serializedArray == null) return;

			// Show context menu entry only if the user can move it up or down
			if (index > 0)
			{
				menu.AddItem (new GUIContent ("Move Array Element Down"), false,
				              () => MoveDown (serializedTargetObject, serializedArray, index));
			}

			if (index < arrayLength - 1)
			{
				menu.AddItem (new GUIContent ("Move Array Element Up"), false,
				              () => MoveUp (serializedTargetObject, serializedArray, index));
			}
		}

		static public void MoveDown (SerializedObject target, SerializedProperty array, int index)
		{
			array.MoveArrayElement (index, index - 1);
			target.ApplyModifiedProperties ();
		}

		static public void MoveUp (SerializedObject target, SerializedProperty array, int index)
		{
			array.MoveArrayElement (index, index + 1);
			target.ApplyModifiedProperties ();
		}
	}
}

Of course the code is for free use!




← Back