Unity 自定义 Inspector 面板时检测属性被修改

方法

public override void OnInspectorGUI()
{
    
    
    // (前文)
    
    EditorGUI.BeginChangeCheck()// ... 待监控代码块(如绘制各种属性框) ...
    
    if (EditorGUI.EndChangeCheck()) // 代码块内绘制的属性框被修改
    {
    
    
        // ... 立即应用修改,或标记场景已脏
    }
    
	// (后文)
}

效果

  1. 对用户手动在 Inspector 上造成的改变,发生响应;
  2. 若其他脚本修改了该脚本的变量,使其在 Inspector 上造成改变,则不发生响应。

至于为何会产生该效果,我手动实现了一下,参考下列等效代码。

等效示例

protected SerializedProperty count; // 背景:在 Runtime 脚本中有 int 型字段 _count 

protected virtual void OnEnable()
{
    
    
    count = serializedObject.FindProperty("_count");
}

public override void OnInspectorGUI() // 要理解 Inspector 也是逐帧绘制的
{
    
    
    // (前文)
    
    // SerializedProperty 在当前帧的值
    // 若用户手动造成了改变,其实此时还没有进行(下一句才进行),SerializedProperty 内部还是原值;
    // 若是外部脚本造成的改变,则在之前(serializedObject.Update() 时)就完成了,此时 SerializedProperty 内部已经是新值
    int countValue = count.intValue; 
    
    // 将 SerializedProperty 绘制成供用户观察和修改的属性框
    // 用户手动造成的修改,实际上是在此时完成的;SerializedProperty 被更新为新值
    EditorGUILayout.PropertyField(count, new GUIContent("Count", ""));
    
    // 对比 SerializedProperty 当前值和属性框绘制前的值
    // 若不同,则一定是因为用户造成了修改(而不是外部脚本造成的修改)
    if (countValue != count.intValue)
    {
    
    
        // 采取相应方法提前应用修改
        // 因为此时 SerializedProperty 的新值还没有反序列化到 _count 字段中,直到 serializedObject.ApplyModifiedProperties()
        // 执行前 _count 都还是原值;若这个期间内 Runtime 脚本(继承 MonoBehavior 的脚本)内使用了 _count 则都是基于原值的,
        // 可能造成错误,因此存在提前应用修改的必要
        // ...
    }
    
    // (后文)
}

提前应用修改

假设上例中的 count 属性在 Runtime 脚本(即继承了 MonoBehavior 而非 Editor 的脚本)中是这样定义的:

public int count
{
    
    
    get => _count;
    set => _count = Mathf.Max(0, value); // Setter 可被用于提前更新用户修改
}
[SerializeField]
protected int _count = 0;

则可以利用其 Setter 提前应用修改,如下:

public override void OnInspectorGUI()
{
    
    
    // (前文)
    
    EditorGUI.BeginChangeCheck();
    EditorGUILayout.PropertyField(count, new GUIContent("Count", ""));
    if (EditorGUI.EndChangeCheck())
    {
    
    
        // SerializedProperty 的值此时是新的,利用 Runtime 脚本 count 属性的
        // Setter 将其存入字段 _count 实现提前更新,而不必等待最后的反序列化
        ((ExampleScript)target).count = count.intValue;
    }
    
	// (后文)
}

猜你喜欢

转载自blog.csdn.net/xzqsr2011/article/details/121551552