Unity Scriptable Object中List使用问题求助:元素显示异常与数量限制失效
我来帮你解决这两个Inspector面板的小问题,都是Unity默认编辑器行为导致的小坑,咱们一步步来处理:
问题1:第0个元素始终最小化
Unity默认的List绘制器在处理同时带有[TextArea]属性的List元素时,第一个元素的TextArea布局会出问题,导致它默认折叠成最小高度。解决这个问题的最佳方式是写一个自定义PropertyDrawer,手动控制每个List元素的TextArea显示:
- 在你的项目中创建一个新的Editor脚本(比如命名为
TextAreaListDrawer.cs),放在Editor文件夹下(如果没有就新建一个):
using UnityEditor; using UnityEngine; using System.Collections.Generic; [CustomPropertyDrawer(typeof(List<string>), true)] public class TextAreaListDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); // 绘制List的标题和折叠箭头 property.isExpanded = EditorGUI.Foldout(new Rect(position.x, position.y, position.width, 16), property.isExpanded, label); if (property.isExpanded) { EditorGUI.indentLevel++; // 获取List的大小 SerializedProperty sizeProp = property.FindPropertyRelative("Array.size"); EditorGUI.PropertyField(new Rect(position.x, position.y + 18, position.width, 16), sizeProp); // 遍历每个元素,用TextArea绘制 for (int i = 0; i < sizeProp.intValue; i++) { SerializedProperty elementProp = property.GetArrayElementAtIndex(i); Rect elementRect = new Rect(position.x, position.y + 18 * (i + 2), position.width, 40); // 固定40高度,可按需调整 elementProp.stringValue = EditorGUI.TextArea(elementRect, elementProp.stringValue); } EditorGUI.indentLevel--; } EditorGUI.EndProperty(); } // 重写高度计算,确保足够显示所有元素 public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { if (!property.isExpanded) { return 16; // 折叠状态下的高度 } SerializedProperty sizeProp = property.FindPropertyRelative("Array.size"); return 16 + 18 + (sizeProp.intValue * 40); // 标题+大小字段+每个元素40高度 } }
这个Drawer会强制每个List元素都以固定高度的TextArea显示,第一个元素就不会再默认最小化了。
问题2:Range属性无法限制List长度
[Range(1,5)]是用来限制单个数值字段范围的,对List的元素数量完全不起作用——这是属性的设计逻辑问题。要限制List最多5个元素,有两种简单的解决方法:
方法1:用OnValidate自动截断(简单快捷)
在你的Scriptable Object类中添加OnValidate()方法,每次Inspector里的数值变化时自动检查并截断List:
using UnityEngine; using System.Collections.Generic; [CreateAssetMenu(fileName = "NewMCQ", menuName = "MCQ/Question")] public class MCQQuestion : ScriptableObject { [SerializeField] [TextArea(1,2)] List<string> answerList = new List<string>(); void OnValidate() { if (answerList == null) { answerList = new List<string>(); return; } // 限制最多5个元素,超过就截断 if (answerList.Count > 5) { answerList.RemoveRange(5, answerList.Count - 5); Debug.LogWarning($"[{name}] Answer list can't have more than 5 elements. Truncated to 5."); } // 可选:限制最少1个元素,对应Range的最小值 if (answerList.Count < 1) { answerList.Add(""); Debug.LogWarning($"[{name}] Answer list must have at least 1 element. Added empty entry."); } } }
这样用户即使手动添加超过5个元素,Unity也会自动把多余的删掉,还会给出警告提示。
方法2:自定义Drawer阻止添加(更友好的交互)
如果想让用户根本无法添加超过5个元素,可以修改刚才的TextAreaListDrawer,在List大小达到5时隐藏添加按钮:
在Drawer的OnGUI方法里,绘制sizeProp的时候加个判断:
// 绘制List的大小,当数量达到5时禁用输入 if (sizeProp.intValue >= 5) { EditorGUI.BeginDisabledGroup(true); EditorGUI.PropertyField(new Rect(position.x, position.y + 18, position.width, 16), sizeProp); EditorGUI.EndDisabledGroup(); // 额外加个提示文本 EditorGUI.LabelField(new Rect(position.x, position.y + 36, position.width, 16), "Max 5 answers allowed", EditorStyles.miniLabel); } else { EditorGUI.PropertyField(new Rect(position.x, position.y + 18, position.width, 16), sizeProp); }
这样用户就不能把List大小调到5以上,从根源上阻止了多余元素的创建。
最终建议
如果追求快速解决,用**方法1(OnValidate)**就足够了;如果想要更流畅的Inspector体验,就结合自定义Drawer同时解决两个问题。记得把自定义Drawer脚本放在Editor文件夹下,否则打包的时候会报错哦!
内容的提问来源于stack exchange,提问作者LokeshT

