自定义 Unity 组件属性面板

1,224 阅读7分钟

自定义 Unity 组件属性面板

为了开始学习今天的课程,我们现在 Unity 工程里面新建一个脚本,名字就叫做 CustomInspector好了。我们在场景中新建一个空的游戏物体,将这个脚本挂载上去。

image-20230516091933333

此时我们新建的脚本 CustomInspector 组件的面板只显示一个脚本引用的位置,如果我们双击输入框,系统会高亮脚本在 Assets 中的位置,并且用设定的编辑器打开这个脚本。

Unity 属性面板中支持的属性类型

属性类型\具体类型
基本类型整数(int/uint/long/ulong/short/ushort/byte/sbyte)浮点数(float/double)布尔值(bool)字符串(string)
枚举类型枚举类型(enum)
Unity 特定类型游戏对象(GameObject)组件(Component)资源(Object)预制体(Prefab)
向量和坐标二维向量(Vector2)三维向量(Vector3)四维向量(Verctor4)二维坐标(Rect)
颜色和纹理颜色(Color)纹理(Texture)
数组和列表数组(Array)列表List
其他类型Transform(位置、旋转、缩放)AnimationCurve(动画曲线)LayerMask(层级)

除了上面常见的属性类型还有下面其他常见的属性类型

其他属性类型\具体类型
基本数组类型的数组和列表int[]、Listfloat[]、Listbool[]、List
自定义类需要通过 [System.Serializable] 进行声明
复杂类型和结构体Quaternion(四元数)Matrix4x4(矩阵)Bounds(边界信息)
高级数据类型Gradient(颜色渐变)
物理相关类型RigidbodyColliderJoint

下面是具体的示例代码

基本类型(整数/浮点数/布尔/字符串)

#region 基本类型(整数/浮点数/布尔/字符串)
public int intValue;
public uint uintValue;
public long longValue;
public ulong ulongValue;
public short shortValue;
public ushort ushortValue;
public byte byteValue;
public sbyte sbyteValue;

public float floatValue;
public double doubleValue;

public bool boolValue;
public string stringValue;
#endregion
image-20230516094742384

枚举类型

#region 枚举类型
public RuntimePlatform platform;
#endregion
image-20230516095100126

特定类型

#region 特定类型
// 游戏物体
public GameObject gameObjectValue;
// 组件
public MonoBehaviour customInspector;
// 其他对象
public Object objectValue;
// 预制体和游戏物体一样
public GameObject prefab;
#endregion
image-20230516095422442

向量和坐标

#region 向量和坐标
public Vector2 vector2Value;
public Vector3 vector3Value;
public Vector4 vector4Value;
public Rect rectValue;
#endregion
image-20230516095640329

颜色和纹理

#region 颜色和纹理
public Color colorValue;
public Texture2D texture2DValue;
public Texture3D texture3DValue;
#endregion
image-20230516095840774

数组和列表

#region 数组和列表
public Object[] array;
public List<Object> list;
#endregion
image-20230516100110976

其他类型

#region 其他类型
public Transform transformValue;
public AnimationCurve animationCurveValue;
public LayerMask layerMaskValue;
#endregion
image-20230516100438498

自定义类型

[System.Serializable]
public class MyCustomValue
{
    public int age;
    public string name;
}

#region 自定义类型
public MyCustomValue myCustomValue;
#endregion

自定义类前往别忘记添加 Public

image-20230516101149280

字段的显示和隐藏

我们上面定义了很多面板的属性字段,都是定义的 Public 的字段。这个把全部的字段暴露给外部的调用者是十分不明智的。在平时的开发中,我们要严格的控制每个字段的权限。

假如我们将 MyCustomValue设置成 private

private MyCustomValue myCustomValue;
image-20230516102602783

哇呜,我们面板中的属性竟然被隐藏掉了,怎么做到在面板可以配置,但是无法被外面访问呢?

SerializeField

可以让未公开的字段可以现在在属性面板上面

[SerializeField]
private MyCustomValue myCustomValue;

我们前往 Unity 发现我们的 myCustomValue 又回来了。

现在我们可能还会遇到一个需求,那就是我们想公开一个变量,但是不允许通过属性面板来让修改,这个怎么做呢?

HideInInspector

可以让公开的字段不出现在属性面板上面

[HideInInspector]
public int intValue;

通过字段精细化控制

Range(min, max)

将数值类型字段限制在指定范围内,并在属性面板中显示为滑动条或数字输入框。

// 设置年龄段只能在18到100岁
[Range(18, 100)]
public int age;
image-20230516105939479

TextArea(minLines,maxLines)

将字符串字段显示为指定行数的文本区域,以便在属性面板中进行多行文本编辑。

// 设置文本输入的高度最小是1,最大是3
[TextArea(1, 3)]
public string description;
image-20230516110402880

Header(string)

在属性面板中为字段添加标题,以提高可读性和组织性

// 显示字段的标题
[Header("设置玩家的名称")]
public string playerName;
image-20230516110627590

Tooltip(string)

在属性面板中为字段添加工具提示,当鼠标悬停在字段上时显示相关说明。

// 显示鼠标悬停提示文本
[Tooltip("最大的血量")]
public int maxHealth;

Space(height)

在属性面板中添加指定高度的空白间隔,用于视觉分隔和布局调整

// 距离上一个属性分割10像素
[Space(10)]
public int score;
image-20230516111211963

ColorUsage

用于控制在Unity属性面板中显示颜色字段的方式。它提供了更多的控制选项,允许您定义颜色的显示和范围。

[ColorUsage] 属性有几个常用的参数:

  1. showAlpha(默认为true):指定是否在属性面板中显示颜色的透明度(Alpha)通道。
  2. hdr(默认为false):指定是否将颜色视为高动态范围(HDR)颜色。如果设置为true,颜色字段将支持更高的动态范围和更大的颜色值范围。
  3. minBrightnessmaxBrightness:指定颜色亮度的最小值和最大值,用于限制颜色的亮度范围。
  4. minExposuremaxExposure:指定颜色曝光值的最小值和最大值,用于限制颜色的曝光范围。
  5. colorSpace(默认为ColorSpace.Uninitialized):指定颜色空间,可以是ColorSpace.Gamma(伽马校正)或ColorSpace.Linear(线性颜色空间)。
[ColorUsage(showAlpha: true, hdr: true)]
public Color hdrColor;
image-20230516111947124

Delayed

用于延迟应用字段的更改,直到用户结束编辑或按下回车键

// 延时更改此字段的值
[Delayed]
public int delayedValue;
image-20230516112201768

此时修改值不会因为值得变动引起一些逻辑调用,影响性能。

Min

设置最小值

[Min(18)]
public int minAge;
image-20230516112447642

Multiline

用于字符串字段,将其显示为多行文本输入框,以便在属性面板中进行多行编辑

[Multiline(5)]
public string multilineText;
image-20230516112642271

此时输入框固定高度就是5行。

实现可折叠面板

为了可以实现可折叠的面板,我们新建一个脚本类 FoldoutInspector。我们需要通过自定义 Editor 实现,新增一个 FoldoutInspector 类继承于 Editor

// 设置 [FoldoutInspector] 进行自定义属性面板
[CustomEditor(typeof(FoldoutInspector))]
public class MyComponentEditor : Editor {}

实现 OnInspectorGUI 方法

监听折叠状态

通过方法 EditorGUILayout.BeginFoldoutHeaderGroup 监听折叠状态

// [EditorGUILayout.BeginFoldoutHeaderGroup] 这个方法改变 [myComponent.foldout] 值
// [Properties] 是这个折叠面板的名字
myComponent.foldout = EditorGUILayout.BeginFoldoutHeaderGroup(myComponent.foldout, "Properties");

根据折叠的状态用代码控制显示的字段

// 根据折叠面板的状态,显示或隐藏属性字段
if (myComponent.foldout)
{
  	// 除了 [EditorGUILayout.TextField] 还有其他的组件方法
    myComponent.propertyName1 = EditorGUILayout.TextField("Property 1", myComponent.propertyName1);
    myComponent.propertyName2 = EditorGUILayout.TextField("Property 2", myComponent.propertyName2);
}

当在自定义编辑器中绘制属性字段时,Unity提供了多种方法和控件来适应不同类型的数据。以下是一些常用的方法和对应的控件:

方法控件说明
EditorGUILayout.LabelField标签显示只读文本或标签
EditorGUILayout.Toggle开关显示布尔值的开关
EditorGUILayout.IntField整数输入框接受整数值输入
EditorGUILayout.FloatField浮点数输入框接受浮点数值输入
EditorGUILayout.EnumPopup枚举下拉菜单显示枚举类型的选项
EditorGUILayout.Slider滑动条显示数值范围内的滑动条
EditorGUILayout.ColorField颜色选择器显示颜色选择器
EditorGUILayout.ObjectField对象字段显示对象选择器
EditorGUILayout.Vector2Field二维向量输入框接受二维向量值输入
EditorGUILayout.Vector3Field三维向量输入框接受三维向量值输入
EditorGUILayout.Vector4Field四维向量输入框接受四维向量值输入
EditorGUILayout.BoundsField包围盒输入框接受包围盒数据输入
EditorGUILayout.RectField矩形输入框接受矩形数据输入
EditorGUILayout.TextureField纹理选择器显示纹理选择器
EditorGUILayout.GradientField渐变选择器显示渐变选择器
EditorGUILayout.CurveField曲线编辑器显示曲线编辑器
EditorGUILayout.MaskField掩码字段显示掩码选择器
EditorGUILayout.TextArea文本区域输入框显示多行文本输入框
EditorGUILayout.Space空白行在属性面板中添加空白行
EditorGUILayout.HelpBox帮助框显示帮助文本和图标

在分组的最后添加结束标志并且刷新当前界面。

EditorGUILayout.EndFoldoutHeaderGroup();

// 应用修改
serializedObject.ApplyModifiedProperties();

脚本 Inspector 面板可以自定义

通过上面的我们通过 EditorGUILayout 做到了折叠的功能,那么我们可以自定义页面吗?

答案是可以的,比如我们将一个测试好的模型上传到服务器,可以这样绘制面板。

public class CustomInspectorWindow : MonoBehaviour
{
    [Header("模型的标题")]
    public string title;
    [Header("上传的模型")]
    public GameObject gameObjectValue;
}

[CustomEditor(typeof(CustomInspectorWindow))]
public class CustomInspectorWindowEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        if (GUILayout.Button("上传模型"))
        {
            // 上传模型的逻辑
            Debug.Log("上传模型 Success");
        }
    }
}
image-20230516135841467

其实对于一些自定义的可以用自定义编辑器部分来做了。