You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity自定义属性抽屉问题:下拉菜单修改时数组内所有元素值同步变更

Unity自定义属性抽屉问题:下拉菜单修改时数组内所有元素值同步变更

嘿,我来帮你搞定这个头疼的问题!你遇到的所有元素同步变更的情况,根源在于自定义属性抽屉的成员变量被多个属性实例共享了,咱们一步步拆解解决:

问题原因分析

你在SoundNamePropertyDrawer里把_nameProperty_indexProperty这些属性引用定义成了类的成员变量,但Unity的CustomPropertyDrawer实例是会被复用的——比如当你在数组里有多个SoundName元素时,所有元素的绘制工作都会用同一个Drawer实例。

这就会导致:当绘制第二个元素时,_nameProperty会被替换成第二个元素的Name属性引用,之前的元素再刷新时,用的已经是新的引用了,结果就是所有元素的属性值都被同步成最后一次修改的那个值。

解决方案:避免成员变量共享属性引用

核心修改点就是:不要把每个属性独有的SerializedProperty存在Drawer的实例成员变量里,而是每次OnGUI调用时,都基于传入的property参数重新获取对应的子属性。另外资源加载部分可以用静态变量做缓存(因为资源是全局唯一的),避免重复加载。

修改后的完整代码示例:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(SoundName))]
public class SoundNamePropertyDrawer : PropertyDrawer
{
    // 用静态变量缓存资源,避免重复加载(全局唯一,不会有共享问题)
    private static SoundNamesContainer _soundNamesContainer;

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // 每次绘制都从当前传入的property获取子属性,确保对应当前要绘制的元素
        SerializedProperty nameProperty = property.FindPropertyRelative("Name");
        SerializedProperty indexProperty = property.FindPropertyRelative("index");

        // 加载资源(静态变量只会初始化一次)
        if (_soundNamesContainer == null)
        {
            _soundNamesContainer = Resources.Load<SoundNamesContainer>("SoundNames");
            if (_soundNamesContainer == null)
            {
                Debug.LogError("找不到Resources目录下的SoundNames资源!");
                return;
            }
        }

        // 这里写你的下拉菜单逻辑,比如用EditorGUI.Popup
        string[] soundOptions = _soundNamesContainer.soundNames; // 假设你的容器类里有这个字符串数组
        int currentIndex = indexProperty.intValue;

        // 确保索引在有效范围内,防止越界
        if (currentIndex < 0 || currentIndex >= soundOptions.Length)
        {
            currentIndex = 0;
            indexProperty.intValue = currentIndex;
            nameProperty.stringValue = soundOptions[currentIndex];
        }

        // 绘制下拉菜单并更新值
        currentIndex = EditorGUI.Popup(position, label.text, currentIndex, soundOptions);
        if (currentIndex != indexProperty.intValue)
        {
            indexProperty.intValue = currentIndex;
            nameProperty.stringValue = soundOptions[currentIndex];
        }
    }

    // 可选:重写这个方法设置属性行的高度
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUIUtility.singleLineHeight;
    }
}

额外提示

  • 如果你之前的下拉菜单逻辑有其他自定义处理,只需要把原来用到_nameProperty_indexProperty的地方,替换成本次OnGUI里新获取的局部变量即可。
  • 静态变量_soundNamesContainer是安全的,因为它缓存的是全局唯一的资源实例,所有属性绘制都用同一个资源,不会导致属性值混乱。

备注:内容来源于stack exchange,提问作者Stefan Carpeliuc

火山引擎 最新活动