You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity如何重写PropertyDrawer实现列表/数组的自定义拖放验证?

Unity 自定义列表/数组拖放验证,覆盖默认头部拖放逻辑

核心思路

要完全覆盖列表/数组的拖放行为,不能只针对单个元素写PropertyDrawer,必须为整个列表/数组实现自定义PropertyDrawer,拦截拖放事件并复用你已有的验证逻辑,同时保留Unity默认的列表UI样式。

实现步骤

1. 定义标记属性(可选但更灵活)

创建一个自定义Attribute,用来标记需要启用自定义拖放验证的列表/数组,这样可以选择性地应用到目标字段上:

using UnityEngine;

public class ValidatedListAttribute : PropertyAttribute { }

2. 实现列表的自定义PropertyDrawer

编写对应的Drawer类,处理拖放事件并复用验证逻辑:

using UnityEditor;
using UnityEngine;
using System.Collections.Generic;

[CustomPropertyDrawer(typeof(ValidatedListAttribute))]
public class ValidatedListDrawer : PropertyDrawer
{
    // 复用你之前的单个元素验证逻辑,抽成公共方法避免重复代码
    private static bool IsDraggedObjectValid(Object draggedObj)
    {
        // 这里替换成你自己的验证逻辑,比如检查组件、标签、自定义条件等
        ExampleTargetComponent targetComp = draggedObj as ExampleTargetComponent;
        return targetComp != null && targetComp.IsQualified; // 示例验证条件
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // 先绘制Unity默认的列表UI(包括头部和所有元素)
        EditorGUI.PropertyField(position, property, label, true);

        // 拦截当前列表区域内的拖放事件
        Event currentEvent = Event.current;
        if (!position.Contains(currentEvent.mousePosition)) return;

        // 处理拖放更新事件:给用户显示拖放状态(允许/拒绝)
        if (currentEvent.type == EventType.DragUpdated)
        {
            bool hasValidObjects = false;
            foreach (Object obj in DragAndDrop.objectReferences)
            {
                if (IsDraggedObjectValid(obj))
                {
                    hasValidObjects = true;
                    break;
                }
            }
            DragAndDrop.visualMode = hasValidObjects ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
            currentEvent.Use();
        }
        // 处理拖放完成事件:仅添加通过验证的对象
        else if (currentEvent.type == EventType.DragPerform)
        {
            DragAndDrop.AcceptDrag();

            foreach (Object draggedObj in DragAndDrop.objectReferences)
            {
                if (IsDraggedObjectValid(draggedObj))
                {
                    // 向列表末尾添加通过验证的对象
                    property.arraySize++;
                    SerializedProperty newElement = property.GetArrayElementAtIndex(property.arraySize - 1);
                    newElement.objectReferenceValue = draggedObj;
                }
                // 未通过验证的对象直接忽略
            }

            // 保存修改并标记事件已处理,防止默认逻辑触发
            property.serializedObject.ApplyModifiedProperties();
            currentEvent.Use();
        }
    }

    // 确保列表展开时的高度计算正确
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUI.GetPropertyHeight(property, label, true);
    }
}

3. 应用到目标列表字段

在你的MonoBehaviour或ScriptableObject的列表字段上添加[ValidatedList]标记:

using UnityEngine;
using System.Collections.Generic;

public class ExampleManager : MonoBehaviour
{
    [ValidatedList]
    public List<ExampleTargetComponent> validatedComponentList;
}

关键注意事项

  • 复用验证逻辑:把单个元素的验证逻辑抽成公共方法,确保列表拖放和单个元素拖放的验证规则完全一致,避免逻辑不一致。
  • 拦截事件:必须调用currentEvent.Use(),否则Unity默认的拖放逻辑会被触发,导致未验证的对象被添加。
  • 拖放反馈:处理DragUpdated事件,给用户实时的拖放状态提示(比如允许时显示复制光标,拒绝时显示禁止光标),提升编辑器体验。
  • 兼容单个元素Drawer:如果已经实现了单个元素的PropertyDrawer,它会继续作用于列表中的每个元素,两者互不冲突,共同覆盖所有拖放场景。

内容的提问来源于stack exchange,提问作者SagiZiv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:35:19