版本:Unity 6 (6000.0)
语言:英语
使用 IMGUI 创建自定义编辑器窗口
使用 IMGUI 创建自定义编辑器

使用 IMGUI 的属性绘制器来自定义检查器

注意:强烈建议使用 UI 工具包 来扩展 Unity 编辑器,因为它提供比 IMGUI 更现代、灵活和可扩展的解决方案。

属性绘制器可用于通过在您的 脚本一段代码,允许您创建自己的组件,触发游戏事件,随时间推移修改组件属性,并以您喜欢的任何方式响应用户输入。更多信息
查看 词汇表
上使用属性或通过控制特定 Serializable 类应该如何显示来自定义 检查器窗口 中某些控件的外观。

属性绘制器有两种用途

  • 自定义可序列化类的每个实例的 GUI。
  • 使用自定义 属性属性 自定义脚本成员的 GUI。

自定义可序列化类的 GUI

如果您有自定义 Serializable 类,您可以使用 属性绘制器 来控制它在 检查器一个 Unity 窗口,显示有关当前选定游戏对象、资产或项目设置的信息,允许您检查和编辑值。更多信息
查看 词汇表
中的外观。考虑下面脚本示例中的 Serializable 类 Ingredient(注意:这些不是编辑器脚本。属性属性类应放在常规脚本文件中)

C#(示例):

using System;
using UnityEngine;

enum IngredientUnit { Spoon, Cup, Bowl, Piece }

// Custom serializable class
[Serializable]
public class Ingredient
{
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
}

public class Recipe : MonoBehaviour
{
    public Ingredient potionResult;
    public Ingredient[] potionIngredients;
}

使用自定义属性绘制器,可以在检查器中更改 Ingredient 类的每个外观。比较在检查器中使用和不使用自定义属性绘制器的 Ingredient 属性的外观

Class in the Inspector without (left) and with (right) custom Property Drawer.
检查器中使用(右侧)和不使用(左侧)自定义属性绘制器的类。

您可以通过使用 CustomPropertyDrawer 属性并将它要绘制的可序列化类的类型传入来将属性绘制器附加到可序列化类。

C#(示例):

using UnityEditor;
using UnityEngine;

// IngredientDrawer
[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, property);

        // Draw label
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        // Don't make child fields be indented
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        // Calculate rects
        var amountRect = new Rect(position.x, position.y, 30, position.height);
        var unitRect = new Rect(position.x + 35, position.y, 50, position.height);
        var nameRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);

        // Draw fields - pass GUIContent.none to each so they are drawn without labels
        EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
        EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
        EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);

        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

使用属性属性自定义脚本成员的 GUI

属性绘制器 的另一个用途是更改具有自定义 属性属性 的脚本中成员的外观。假设您想将脚本中的浮点数或整数限制在某个范围内,并在 检查器 中以滑块的形式显示它们。使用名为 RangeAttribute 的内置 PropertyAttribute,您可以做到这一点

C#(示例):

// Show this float in the Inspector as a slider between 0 and 10
[Range(0f, 10f)]
float myFloat = 0f;

您也可以创建自己的 PropertyAttribute。我们将使用 RangeAttribute 的代码作为示例。属性必须扩展 PropertyAttribute 类。如果需要,您的属性可以接受参数并将它们存储为公共成员变量。

C#(示例):

using UnityEngine;

public class MyRangeAttribute : PropertyAttribute 
{
        readonly float min;
        readonly float max;
        
        void MyRangeAttribute(float min, float max)
        {
            this.min = min;
            this.max = max;
        }
}

现在您有了属性,您需要创建一个 属性绘制器 来绘制具有该属性的属性。绘制器必须扩展 PropertyDrawer 类,并且必须具有 CustomPropertyDrawer 属性来告诉它它要绘制哪个属性。

属性绘制器类应放在编辑器脚本中,位于名为 Editor 的文件夹中。

C#(示例):

using UnityEditor;
using UnityEngine;

// Tell the MyRangeDrawer that it is a drawer for properties with the MyRangeAttribute.
[CustomPropertyDrawer(typeof(MyRangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // First get the attribute since it contains the range for the slider
        MyRangeAttribute range = (MyRangeAttribute)attribute;

        // Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
        if (property.propertyType == SerializedPropertyType.Float)
            EditorGUI.Slider(position, property, range.min, range.max, label);
        else if (property.propertyType == SerializedPropertyType.Integer)
            EditorGUI.IntSlider(position, property, (int) range.min, (int) range.max, label);
        else
            EditorGUI.LabelField(position, label.text, "Use MyRange with float or int.");
    }
}

请注意,出于性能原因,EditorGUILayout 函数不能与属性绘制器一起使用。

默认对象引用

如果您定义了公共 ObjectUnity 场景中的基本对象,可以代表角色、道具、场景、相机、航点等等。游戏对象的功能由附加到它的组件定义。更多信息
查看 词汇表
字段或标记为 SerializeField 的私有字段,您可以为这些字段设置默认引用。默认引用字段在您在 项目窗口显示您的 `Assets` 文件夹内容的窗口(项目选项卡)更多信息
查看 词汇表
中选择脚本资产时在检查器窗口中可见。

注意:建议在每个 PropertyDrawer 的单独文件中保留其匹配的名称。这确保了有效地分配默认对象引用,因为它们只能分配给一个 PropertyDrawer。当同一个文件中存在多种类型时,分配的类型要么与文件名匹配,要么是文件中定义的第一个类型。

使用 IMGUI 创建自定义编辑器窗口
使用 IMGUI 创建自定义编辑器