公司动态
Unity 编辑器之自定义脚本界面
自定义脚本界面有两种方法一种是继承PropertyDrawer一种是继承Editor。下面介绍继承自 PropertyDrawerPropertyDrawer有两种用途一种是自定义绘制带有Serializable属性的类另一种是自定义继承自PropertyAttribute的类。1. 绘制带有 Serializable 属性的类在重新绘制带有Serializable属性的类时绘制类需要继承自PropertyDrawer并且加上CustomPropertyDrawer特性。下面是一个完整的示例using System; using UnityEngine; public enum IngredientUnit { Spoon, Cup, Bowl, Piece } // 自定义绘制类没有继承 MonoBehaviour [Serializable] public class Ingredient { public string name; public int amount 1; public IngredientUnit unit; } // 继承 MonoBehaviour使用默认绘制 public class Recipe : MonoBehaviour { public Ingredient potionResult; public Ingredient[] potionIngredients; } using UnityEditor; using UnityEditor.UIElements; using UnityEngine.UIElements; // IngredientDrawerUIE [CustomPropertyDrawer(typeof(Ingredient))] public class IngredientDrawerUIE : PropertyDrawer { public override VisualElement CreatePropertyGUI(SerializedProperty property) { // 创建属性容器元素 var container new VisualElement(); // 创建属性字段 var amountField new PropertyField(property.FindPropertyRelative(amount)); var unitField new PropertyField(property.FindPropertyRelative(unit)); var nameField new PropertyField(property.FindPropertyRelative(name), Fancy Name); // 将字段添加到容器中 container.Add(amountField); container.Add(unitField); container.Add(nameField); return container; } }在上面的示例中Ingredient类被标记为[Serializable]因此 Unity 会在 Inspector 中自动显示它的字段。通过自定义PropertyDrawer我们可以完全控制这些字段在 Inspector 中的显示方式例如调整字段顺序、添加自定义标签或实现更复杂的布局。2. 绘制继承自 PropertyAttribute 类的属性比如系统自带的属性Range它可以让数值字段在 Inspector 中显示为滑动条。下面我们自己实现一个和Range属性功能类似的属性using UnityEngine; public class RangeAttribute : PropertyAttribute { public float min; public float max; public RangeAttribute(float min, float max) { this.min min; this.max max; } } [CustomPropertyDrawer(typeof(RangeAttribute))] public class RangeDrawer : PropertyDrawer { // 在给定的矩形区域内绘制属性 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // 首先获取特性其中包含滑动条的范围 RangeAttribute range attribute as RangeAttribute; // 根据属性类型绘制滑动条或整数滑动条 if (property.propertyType SerializedPropertyType.Float) EditorGUI.Slider(position, property, range.min, range.max, label); else if (property.propertyType SerializedPropertyType.Integer) EditorGUI.IntSlider(position, property, Convert.ToInt32(range.min), Convert.ToInt32(range.max), label); else EditorGUI.LabelField(position, label.text, Use Range with float or int.); } }通过继承PropertyAttribute并配合PropertyDrawer我们可以为自定义属性添加丰富的 Inspector 交互体验。这种方式非常适合封装常用的数据校验逻辑例如限制数值范围、格式化字符串或实现下拉选择等。下面介绍继承自 Editor除了使用PropertyDrawer自定义单个属性的绘制方式我们还可以通过继承Editor类来整体自定义某个组件或脚本在 Inspector 中的显示界面。这种方式适用于需要控制整个对象面板布局的场景例如隐藏默认字段、添加按钮、展示自定义信息或实现更复杂的交互。1. 使用 OnInspectorGUI 方法继承Editor类后最核心的方法是重写OnInspectorGUI。Unity 会在 Inspector 面板刷新时调用该方法我们可以在其中使用GUILayout、EditorGUILayout等工具绘制自定义界面。下面是一个简单的示例using UnityEditor; using UnityEngine; [CustomEditor(typeof(Recipe))] public class RecipeEditor : Editor { public override void OnInspectorGUI() { // 绘制默认的 Inspector 内容 DrawDefaultInspector(); // 在默认内容下方添加一个按钮 Recipe recipe (Recipe)target; if (GUILayout.Button(打印配方信息)) { Debug.Log(配方名称 recipe.potionResult.name); Debug.Log(配料数量 recipe.potionIngredients.Length); } } }在上面的示例中DrawDefaultInspector()会先绘制脚本默认的字段随后我们通过GUILayout.Button添加了一个自定义按钮。这样既保留了原有字段的显示又扩展了额外的操作入口。2. 处理多个对象当在 Inspector 中同时选中多个对象时Editor类的targets属性会包含所有被选中的对象。此时我们需要遍历targets来处理多对象场景避免只对第一个对象生效。下面是一个示例using UnityEditor; using UnityEngine; [CustomEditor(typeof(Recipe))] public class RecipeEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); if (GUILayout.Button(批量打印配方信息)) { foreach (Recipe recipe in targets) { Debug.Log(配方名称 recipe.potionResult.name); Debug.Log(配料数量 recipe.potionIngredients.Length); } } } }通过遍历targets我们可以确保批量操作对所有选中的对象生效。此外在处理多对象时还可以使用serializedObject和serializedObject.targetObjects来统一管理序列化属性从而保证修改能够正确应用到每个对象上。3. 与 PropertyDrawer 的主要区别Editor和PropertyDrawer虽然都用于自定义 Inspector 界面但它们的定位和使用场景有明显区别作用范围不同PropertyDrawer只负责绘制某个具体的字段或属性而Editor负责整个组件或脚本的 Inspector 面板。使用方式不同PropertyDrawer通过CustomPropertyDrawer特性绑定到目标属性Editor通过CustomEditor特性绑定到目标脚本。控制粒度不同PropertyDrawer适合精细控制单个字段的显示Editor适合整体布局、添加按钮或实现跨字段的复杂交互。适用场景不同当只需要美化某个字段时优先使用PropertyDrawer当需要重排整个面板、添加操作按钮或处理多对象时应使用Editor。在实际开发中两者经常配合使用用PropertyDrawer控制字段级别的显示细节用Editor控制组件级别的整体布局从而打造出既美观又易用的自定义 Inspector 界面。