Note: It’s strongly recommended to use the UI Toolkit to extend the Unity Editor, as it provides a more modern, flexible, and scalable solution than IMGUI.
为了加快应用程序开发,请为常用组件创建自定义编辑器。本页面展示了如何创建一个简单的脚本以使游戏对象始终看向一个点。
//C# 示例 (LookAtPoint.cs)
using UnityEngine;
public class LookAtPoint : MonoBehaviour
{
public Vector3 lookAtPoint = Vector3.zero;
void Update()
{
transform.LookAt(lookAtPoint);
}
}
进入运行模式后,脚本所附加到的游戏对象现在会将自身朝向设置为“Look At Point”属性的坐标。编写编辑器脚本时,让某些脚本在编辑模式期间执行通常很有用(此时未运行您的应用程序)。为此,请向类添加 ExecuteInEditMode
属性,如下所示:
//C# 示例 (LookAtPoint.cs)
using UnityEngine;
[ExecuteInEditMode]
public class LookAtPoint : MonoBehaviour
{
public Vector3 lookAtPoint = Vector3.zero;
void Update()
{
transform.LookAt(lookAtPoint);
}
}
现在,如果在 Editor 中移动游戏对象或者在 Inspector 中更改“Look At Point”的值,则游戏对象将更新其旋转,以便其看向世界空间中的目标点。
上文演示了如何在编辑时运行简单脚本;但仅凭此还不足以创建自己的 Editor 工具。下一步需要为您刚创建的脚本创建__自定义编辑器__。
在 Unity 中创建脚本时,默认情况下,此脚本继承自 MonoBehaviour,因此可作为附加到游戏对象的组件。将一个组件放置在游戏对象上时,Inspector 会显示一个默认界面,可用于查看和编辑每个公共变量,例如:整数、浮点数或字符串。
LookAtPoint 组件的 Inspector 在默认情况下的外观如下:
自定义编辑器是一个单独脚本,可将此默认布局替换为您选择的任何编辑器控件。
要为 LookAtPoint 脚本创建自定义编辑器,请执行以下操作:
//C# 示例 (LookAtPointEditor.cs)
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(LookAtPoint))]
[CanEditMultipleObjects]
public class LookAtPointEditor : Editor
{
SerializedProperty lookAtPoint;
void OnEnable()
{
lookAtPoint = serializedObject.FindProperty("lookAtPoint");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(lookAtPoint);
serializedObject.ApplyModifiedProperties();
}
}
该类必须继承自 Editor。CustomEditor 属性告知 Unity 应该作为哪个组件的编辑器。CanEditMultipleObjects 属性告诉 Unity 您可以使用此编辑器来选择多个对象并同时更改所有对象。
Unity 在 Inspector 中显示编辑器时将执行 OnInspectorGUI 中的代码。可以在此处插入任何 GUI 代码,原理就像 OnGUI 一样,不过是在 Inspector 内运行。编辑器会定义可用于访问所检查游戏对象的目标属性。
使用新编辑器时,LookAtPoint 组件的 Inspector 的外观如下:
这看起来非常相似(但现在不存在“Script”字段,因为编辑器脚本未添加任何 Inspector 代码来显示它)。
但是,现在可以在编辑器脚本中控制 Inspector 的显示方式,可以使用所喜欢的任何代码来布局 Inspector 字段,允许用户调整值,甚至显示图形或其他可视元素。实际上,在 Unity Editor 中看到的所有 Inspector(包括更复杂的 Inspector,例如地形系统和动画导入设置)都是使用同一 API 生成的。在创建自己的自定义编辑器时可以访问此 API。
下面是一个简单示例,用于扩展编辑器脚本以显示一条消息,指示目标点在游戏对象之上还是之下:
//C# 示例 (LookAtPointEditor.cs)
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(LookAtPoint))]
[CanEditMultipleObjects]
public class LookAtPointEditor : Editor
{
SerializedProperty lookAtPoint;
void OnEnable()
{
lookAtPoint = serializedObject.FindProperty("lookAtPoint");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(lookAtPoint);
serializedObject.ApplyModifiedProperties();
if (lookAtPoint.vector3Value.y > (target as LookAtPoint).transform.position.y)
{
EditorGUILayout.LabelField("(Above this object)");
}
if (lookAtPoint.vector3Value.y < (target as LookAtPoint).transform.position.y)
{
EditorGUILayout.LabelField("(Below this object)");
}
}
}
这就是 LookAtPoint 组件的 Inspector 的外观,其中显示了一条消息来指明目标点在游戏对象之上还是之下。
可以全面访问所有 IMGUI 命令来绘制任何类型的界面,包括使用 Editor 窗口中的摄像机来渲染场景。
您可以向 Scene 视图添加额外的代码。为此,请在自定义编辑器中实现 OnSceneGUI。
OnSceneGUI 的运行方式很像 OnInspectorGUI,只不过在 Scene 视图中运行而已。为了方便您创建自己的编辑控件,可以使用在 Handles 类中定义的函数。其中的所有函数都是为 3D 模式的 Scene 视图设计的。
//C# 示例 (LookAtPointEditor.cs)
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(LookAtPoint))]
[CanEditMultipleObjects]
public class LookAtPointEditor : Editor
{
SerializedProperty lookAtPoint;
void OnEnable()
{
lookAtPoint = serializedObject.FindProperty("lookAtPoint");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(lookAtPoint);
if (lookAtPoint.vector3Value.y > (target as LookAtPoint).transform.position.y)
{
EditorGUILayout.LabelField("(Above this object)");
}
if (lookAtPoint.vector3Value.y < (target as LookAtPoint).transform.position.y)
{
EditorGUILayout.LabelField("(Below this object)");
}
serializedObject.ApplyModifiedProperties();
}
public void OnSceneGUI()
{
var t = (target as LookAtPoint);
EditorGUI.BeginChangeCheck();
Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Move point");
t.lookAtPoint = pos;
t.Update();
}
}
}
如果要添加 2D GUI 对象(例如:GUI 或 EditorGUI),需要将这些对象包裹在 Handles.BeginGUI() 和 Handles.EndGUI() 的调用中。