Search Unity

Generic Editor Array PropertyAttribute Tools

Discussion in 'Scripting' started by cfloutier, Apr 16, 2014.

  1. cfloutier

    cfloutier

    Joined:
    Jul 30, 2009
    Posts:
    35
    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 :

    $Example.png

    It works with primitives and with Classes !!!

    First adds a class ArrayAttribute.cs :
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ArrayAttribute : PropertyAttribute
    6. {}
    7.  
    and a Property drawer inside an Editor folder
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnityEditor;
    5.  
    6. // The property drawer class should be placed in an editor script, inside a folder called Editor.
    7. // Tell the ArrayDrawer that it is a drawer for properties with the ArrayAttribute.
    8. [CustomPropertyDrawer(typeof(ArrayAttribute))]
    9. public class ArrayDrawer : PropertyDrawer
    10. {
    11.     const float widthBt = 35;
    12.  
    13.     void addArrayTools(Rect position, SerializedProperty property)
    14.     {
    15.         string path = property.propertyPath;
    16.         int arrayInd = path.LastIndexOf(".Array");
    17.         bool bIsArray = arrayInd >= 0;
    18.  
    19.         if (bIsArray)
    20.         {
    21.             SerializedObject so = property.serializedObject;
    22.             string arrayPath = path.Substring(0, arrayInd);
    23.             SerializedProperty arrayProp = so.FindProperty(arrayPath);
    24.  
    25.             //Next we need to grab the index from the path string
    26.             int indStart = path.IndexOf("[") + 1;
    27.             int indEnd = path.IndexOf("]");
    28.  
    29.             string indString = path.Substring(indStart, indEnd - indStart);
    30.  
    31.             int myIndex = int.Parse(indString);
    32.             Rect rcButton = position;
    33.             rcButton.height = EditorGUIUtility.singleLineHeight;
    34.             rcButton.x = position.xMax - widthBt * 4;
    35.             rcButton.width = widthBt;
    36.  
    37.             bool lastEnabled = GUI.enabled;
    38.  
    39.             if (myIndex == 0)
    40.                 GUI.enabled = false;
    41.  
    42.             if (GUI.Button(rcButton, "Up"))
    43.             {
    44.                 arrayProp.MoveArrayElement(myIndex, myIndex - 1);
    45.                 so.ApplyModifiedProperties();
    46.                
    47.             }
    48.  
    49.             rcButton.x += widthBt;
    50.             GUI.enabled = lastEnabled;
    51.             if (myIndex >= arrayProp.arraySize - 1)
    52.                 GUI.enabled = false;
    53.  
    54.             if (GUI.Button(rcButton, "Dn"))
    55.             {
    56.                 arrayProp.MoveArrayElement(myIndex, myIndex + 1);
    57.                 so.ApplyModifiedProperties();
    58.             }
    59.  
    60.             GUI.enabled = lastEnabled;
    61.  
    62.             rcButton.x += widthBt;
    63.             if (GUI.Button(rcButton, "Del"))
    64.             {
    65.                 arrayProp.DeleteArrayElementAtIndex(myIndex);
    66.                 so.ApplyModifiedProperties();
    67.             }
    68.  
    69.             rcButton.x += widthBt;
    70.             if (GUI.Button(rcButton, "Ins"))
    71.             {
    72.                 arrayProp.InsertArrayElementAtIndex(myIndex);
    73.                 so.ApplyModifiedProperties();
    74.             }
    75.         }
    76.     }
    77.  
    78.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    79.     {
    80.         addArrayTools(position, property);
    81.         Rect rc = position;
    82.         if (!property.isExpanded)
    83.             rc.width -= widthBt * 4;
    84.  
    85.         EditorGUI.PropertyField(rc, property, label, true);
    86.     }
    87.  
    88.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    89.     {
    90.         return EditorGUI.GetPropertyHeight(property);
    91.     }
    92. }
    93.  

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

    example :

    Code (csharp):
    1.  
    2.     [Array]
    3.     public MyClass[] theArray;
    4.  
    5.     [Array]
    6.     public int[] theIntArray;
    7.  

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

    I hope It could help.
     

    Attached Files:

    Grhyll, RV1 and Jethro like this.
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
  3. Jethro

    Jethro

    Joined:
    Aug 23, 2011
    Posts:
    30
    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 @Array 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.
     
  4. Jethro

    Jethro

    Joined:
    Aug 23, 2011
    Posts:
    30
    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.
     
  5. Carrotpie

    Carrotpie

    Joined:
    Sep 25, 2014
    Posts:
    30
    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.