方法
public override void OnInspectorGUI()
{
// (前文)
EditorGUI.BeginChangeCheck();
// ... 待监控代码块(如绘制各种属性框) ...
if (EditorGUI.EndChangeCheck()) // 代码块内绘制的属性框被修改
{
// ... 立即应用修改,或标记场景已脏
}
// (后文)
}
效果
- 对用户手动在 Inspector 上造成的改变,发生响应;
- 若其他脚本修改了该脚本的变量,使其在 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;
}
// (后文)
}