自定义 Unity 组件属性面板
为了开始学习今天的课程,我们现在 Unity 工程里面新建一个脚本,名字就叫做 CustomInspector好了。我们在场景中新建一个空的游戏物体,将这个脚本挂载上去。
此时我们新建的脚本 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[]、List | float[]、List | bool[]、List |
| 自定义类 | 需要通过 [System.Serializable] 进行声明 | ||
| 复杂类型和结构体 | Quaternion(四元数) | Matrix4x4(矩阵) | Bounds(边界信息) |
| 高级数据类型 | Gradient(颜色渐变) | ||
| 物理相关类型 | Rigidbody | Collider | Joint |
下面是具体的示例代码
基本类型(整数/浮点数/布尔/字符串)
#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
枚举类型
#region 枚举类型
public RuntimePlatform platform;
#endregion
特定类型
#region 特定类型
// 游戏物体
public GameObject gameObjectValue;
// 组件
public MonoBehaviour customInspector;
// 其他对象
public Object objectValue;
// 预制体和游戏物体一样
public GameObject prefab;
#endregion
向量和坐标
#region 向量和坐标
public Vector2 vector2Value;
public Vector3 vector3Value;
public Vector4 vector4Value;
public Rect rectValue;
#endregion
颜色和纹理
#region 颜色和纹理
public Color colorValue;
public Texture2D texture2DValue;
public Texture3D texture3DValue;
#endregion
数组和列表
#region 数组和列表
public Object[] array;
public List<Object> list;
#endregion
其他类型
#region 其他类型
public Transform transformValue;
public AnimationCurve animationCurveValue;
public LayerMask layerMaskValue;
#endregion
自定义类型
[System.Serializable]
public class MyCustomValue
{
public int age;
public string name;
}
#region 自定义类型
public MyCustomValue myCustomValue;
#endregion
自定义类前往别忘记添加
Public
字段的显示和隐藏
我们上面定义了很多面板的属性字段,都是定义的 Public 的字段。这个把全部的字段暴露给外部的调用者是十分不明智的。在平时的开发中,我们要严格的控制每个字段的权限。
假如我们将 MyCustomValue设置成 private。
private MyCustomValue myCustomValue;
哇呜,我们面板中的属性竟然被隐藏掉了,怎么做到在面板可以配置,但是无法被外面访问呢?
SerializeField
可以让未公开的字段可以现在在属性面板上面
[SerializeField]
private MyCustomValue myCustomValue;
我们前往 Unity 发现我们的 myCustomValue 又回来了。
现在我们可能还会遇到一个需求,那就是我们想公开一个变量,但是不允许通过属性面板来让修改,这个怎么做呢?
HideInInspector
可以让公开的字段不出现在属性面板上面
[HideInInspector]
public int intValue;
通过字段精细化控制
Range(min, max)
将数值类型字段限制在指定范围内,并在属性面板中显示为滑动条或数字输入框。
// 设置年龄段只能在18到100岁
[Range(18, 100)]
public int age;
TextArea(minLines,maxLines)
将字符串字段显示为指定行数的文本区域,以便在属性面板中进行多行文本编辑。
// 设置文本输入的高度最小是1,最大是3
[TextArea(1, 3)]
public string description;
Header(string)
在属性面板中为字段添加标题,以提高可读性和组织性
// 显示字段的标题
[Header("设置玩家的名称")]
public string playerName;
Tooltip(string)
在属性面板中为字段添加工具提示,当鼠标悬停在字段上时显示相关说明。
// 显示鼠标悬停提示文本
[Tooltip("最大的血量")]
public int maxHealth;
Space(height)
在属性面板中添加指定高度的空白间隔,用于视觉分隔和布局调整
// 距离上一个属性分割10像素
[Space(10)]
public int score;
ColorUsage
用于控制在Unity属性面板中显示颜色字段的方式。它提供了更多的控制选项,允许您定义颜色的显示和范围。
[ColorUsage] 属性有几个常用的参数:
showAlpha(默认为true):指定是否在属性面板中显示颜色的透明度(Alpha)通道。hdr(默认为false):指定是否将颜色视为高动态范围(HDR)颜色。如果设置为true,颜色字段将支持更高的动态范围和更大的颜色值范围。minBrightness和maxBrightness:指定颜色亮度的最小值和最大值,用于限制颜色的亮度范围。minExposure和maxExposure:指定颜色曝光值的最小值和最大值,用于限制颜色的曝光范围。colorSpace(默认为ColorSpace.Uninitialized):指定颜色空间,可以是ColorSpace.Gamma(伽马校正)或ColorSpace.Linear(线性颜色空间)。
[ColorUsage(showAlpha: true, hdr: true)]
public Color hdrColor;
Delayed
用于延迟应用字段的更改,直到用户结束编辑或按下回车键
// 延时更改此字段的值
[Delayed]
public int delayedValue;
此时修改值不会因为值得变动引起一些逻辑调用,影响性能。
Min
设置最小值
[Min(18)]
public int minAge;
Multiline
用于字符串字段,将其显示为多行文本输入框,以便在属性面板中进行多行编辑
[Multiline(5)]
public string multilineText;
此时输入框固定高度就是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");
}
}
}
其实对于一些自定义的可以用自定义编辑器部分来做了。