Unity中PropertyDrawer.GetPropertyHeight在数组元素变更时未被调用
Unity自定义曲线工具数组/列表元素切换时字段高度不刷新问题解决
问题背景
为解决曲线工具在数组中绘制的问题重写了工具,但在数组与列表元素的编辑器间切换时出现异常:已编辑元素会保留其高度,直到编辑集合内其他元素或曲线字段。尝试使用EditorUtility.SetDirty(targetObject)(搭配或不搭配EditorGUI.BeginChangeCheck()/EditorGUI.EndChangeCheck())均无法触发Unity刷新字段高度。
左侧数字为OtherCurve.z,用于区分调试的PropertyDrawer,标签为临时标签。
测试代码:
public class OtherTestScript : MonoBehaviour { public OtherCurve curve; public OtherCurve[] curveArray; public List<OtherCurve> curveList; }
问题相关代码需放入Assets/Plugins/,并在Packages/manifest.json中添加以下依赖:
"st.one-line": "https://github.com/slavniyteo/one-line.git#master", "st.rect-ex": "https://github.com/slavniyteo/rect-ex.git#master",
问题原因
Unity Inspector会缓存PropertyDrawer的高度值,当切换数组/列表的元素编辑器时,Unity不会主动重新计算未修改元素的高度,导致之前编辑元素的高度被错误保留。EditorUtility.SetDirty仅标记对象需要保存,无法触发Inspector的布局刷新。
解决方案
1. 强制每次重新计算属性高度
在自定义PropertyDrawer的GetPropertyHeight方法中,通过生成唯一控制ID,强制Unity放弃高度缓存,每次都根据当前属性状态重新计算高度:
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { // 生成唯一控制ID,强制Unity重新计算高度 EditorGUIUtility.GetControlID(FocusType.Passive, property.propertyPath); // 替换为你的实际高度计算逻辑 var curve = (OtherCurve)fieldInfo.GetValue(property.serializedObject.targetObject); return curve.IsExpanded ? 220f : 24f; // 示例:展开时高220,折叠时高24 }
2. 检测元素切换并触发Inspector重绘
在PropertyDrawer的OnGUI方法中,记录当前属性路径,当检测到路径变化(即切换了数组/列表元素)时,触发当前Inspector窗口重绘:
private string _lastPropertyPath; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); // 检测是否切换了数组/列表元素 var currentPath = property.propertyPath; if (_lastPropertyPath != currentPath) { _lastPropertyPath = currentPath; // 触发当前聚焦的Inspector窗口重绘 EditorWindow.focusedWindow?.Repaint(); } // 你的原有绘制逻辑 var curve = (OtherCurve)fieldInfo.GetValue(property.serializedObject.targetObject); // ... 绘制曲线、编辑字段的代码 ... EditorGUI.EndProperty(); }
3. 属性修改后触发全局重绘
若上述方法仍无效,可在属性修改完成后,通过延迟调用触发所有视图重绘,确保布局更新:
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginChangeCheck(); EditorGUI.BeginProperty(position, label, property); // 你的原有绘制逻辑 var curve = (OtherCurve)fieldInfo.GetValue(property.serializedObject.targetObject); // ... 绘制代码 ... EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck()) { property.serializedObject.ApplyModifiedProperties(); // 延迟调用避免当前布局周期内重绘冲突 EditorApplication.delayCall += () => EditorApplication.RepaintAllViews(); } }
内容的提问来源于stack exchange,提问作者zORg Alex
相关产品推荐
相关产品推荐

