Unity中能否让CustomPropertyDrawer同时支持List与Array类型?
Got it, I totally get where you're coming from—Unity's default List/Array drawer gets the job done but is pretty bland, and it's frustrating that you can't directly target List<> or Array with CustomPropertyDrawer using that OR syntax. Let's break down two solid solutions to fix this:
Option 1: Use a Custom Attribute (Flexible & Targeted)
This approach lets you selectively apply your fancy drawer to specific Lists/Arrays by tagging them with a custom attribute. It's clean and avoids messing with Unity's default behavior for other arrays.
Step 1: Create the Custom Attribute
First, make a simple attribute class to mark the properties you want to style:
using UnityEngine; // This attribute marks Lists/Arrays for our custom drawer public class StyledListAttribute : PropertyAttribute {}
Step 2: Build the Custom Property Drawer
Next, write the drawer that targets this attribute. We'll check if the property is an array/list, then render our custom UI:
using UnityEditor; using UnityEngine; [CustomPropertyDrawer(typeof(StyledListAttribute))] public class StyledListDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // Only process arrays/lists—fallback to default for other types if (!property.isArray) { EditorGUI.PropertyField(position, property, label); return; } EditorGUI.BeginProperty(position, label, property); // Custom foldout header property.isExpanded = EditorGUI.Foldout( new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), property.isExpanded, label ); if (property.isExpanded) { EditorGUI.indentLevel++; // Editable count field Rect countRect = new Rect( position.x, position.y + EditorGUIUtility.singleLineHeight, position.width, EditorGUIUtility.singleLineHeight ); int newCount = EditorGUI.IntField(countRect, "Item Count", property.arraySize); if (newCount != property.arraySize) { property.arraySize = newCount; } // Draw each element with a friendly label for (int i = 0; i < property.arraySize; i++) { SerializedProperty element = property.GetArrayElementAtIndex(i); Rect elementRect = new Rect( position.x, position.y + EditorGUIUtility.singleLineHeight * (2 + i), position.width, EditorGUIUtility.singleLineHeight ); EditorGUI.PropertyField(elementRect, element, new GUIContent($"Item {i + 1}")); } EditorGUI.indentLevel--; } EditorGUI.EndProperty(); } // Calculate the correct height for the expanded list public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { if (!property.isExpanded || !property.isArray) { return EditorGUIUtility.singleLineHeight; } // Header + count field + one line per element return EditorGUIUtility.singleLineHeight * (2 + property.arraySize); } }
Step 3: Apply the Attribute
Just tag any List/Array you want to style with [StyledList]:
public class MyMonoBehaviour : MonoBehaviour { [StyledList] public List<string> dialogueLines; [StyledList] public Vector3[] waypoints; }
Option 2: Global Drawer for All Lists/Arrays (No Attribute Needed)
If you want every single List/Array in your project to use your custom drawer (no tagging required), you can create a drawer that targets all objects, then filters for array types. Note: This will override Unity's default array drawers, so be careful with special types like Sprite[] that have built-in previews.
using UnityEditor; using UnityEngine; // Target all objects, but only process arrays/lists [CustomPropertyDrawer(typeof(object), true)] public class GlobalStyledListDrawer : PropertyDrawer { // Only cache the GUI for array/list properties public override bool CanCacheInspectorGUI(SerializedProperty property) { return property.isArray; } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (!property.isArray) { // Let Unity handle non-array properties EditorGUI.PropertyField(position, property, label); return; } // Use the same custom rendering logic as Option 1 here EditorGUI.BeginProperty(position, label, property); property.isExpanded = EditorGUI.Foldout( new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), property.isExpanded, label ); if (property.isExpanded) { EditorGUI.indentLevel++; Rect countRect = new Rect( position.x, position.y + EditorGUIUtility.singleLineHeight, position.width, EditorGUIUtility.singleLineHeight ); int newCount = EditorGUI.IntField(countRect, "Item Count", property.arraySize); if (newCount != property.arraySize) { property.arraySize = newCount; } for (int i = 0; i < property.arraySize; i++) { SerializedProperty element = property.GetArrayElementAtIndex(i); Rect elementRect = new Rect( position.x, position.y + EditorGUIUtility.singleLineHeight * (2 + i), position.width, EditorGUIUtility.singleLineHeight ); EditorGUI.PropertyField(elementRect, element, new GUIContent($"Item {i + 1}")); } EditorGUI.indentLevel--; } EditorGUI.EndProperty(); } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { if (!property.isExpanded || !property.isArray) { return EditorGUIUtility.singleLineHeight; } return EditorGUIUtility.singleLineHeight * (2 + property.arraySize); } }
Final Notes
- Option 1 is safer for most projects—it lets you pick and choose which lists get the upgrade, so you don't break Unity's default behavior for special array types.
- Option 2 is great for consistency if you want every list in your project to look the same, but test it with different array types (like textures, sprites) to make sure you don't lose useful default features.
内容的提问来源于stack exchange,提问作者KaNaDa

