从此基类派生以创建自定义检查器或您自定义对象的编辑器。
using UnityEngine; using System.Collections;
// This is not an editor script. public class MyPlayer : MonoBehaviour { public int armor = 75; public int damage = 25; public GameObject gun;
void Update() { // Update logic here... } }
例如,使用自定义编辑器更改脚本在检查器中的外观。
您可以使用 CustomEditor 属性将编辑器附加到自定义组件。
有多种方法可以设计自定义编辑器。如果希望编辑器支持多对象编辑,则可以使用 CanEditMultipleObjects 属性。与其直接修改脚本变量,不如使用 SerializedObject 和 SerializedProperty 系统来编辑它们,因为这会自动处理多对象编辑、撤消和预制覆盖。如果使用此方法,用户可以在层次结构窗口中选择多个资源,并同时更改所有资源的值。
您可以使用 UIElements 构建自定义 UI,也可以使用 IMGUI。要使用 UIElements 创建自定义检查器,您必须覆盖 Editor.CreateInspectorGUI Editor 类上的方法。要使用 IMGUI 创建自定义检查器,您必须覆盖 Editor.OnInspectorGUI Editor 类上的方法。如果您使用 UIElements 并且已覆盖 Editor.CreateInspectorGUI ,则在同一编辑器上使用 Editor.OnInspectorGUI 的任何现有的 IMGUI 实现都将被忽略。
这是一个自定义检查器的示例
检查器中的自定义编辑器。
using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; [CustomEditor(typeof(MyPlayer))] public class MyPlayerEditor : Editor { const string resourceFilename = "custom-editor-uie"; public override VisualElement CreateInspectorGUI() { VisualElement customInspector = new VisualElement(); var visualTree = Resources.Load(resourceFilename) as VisualTreeAsset; visualTree.CloneTree(customInspector); customInspector.styleSheets.Add(Resources.Load($"{resourceFilename}-style") as StyleSheet); return customInspector; } }
以下示例定义了 uxml 中自定义检查器的布局。该定义作为资源加载,并且 VisualTreeAsset.CloneTree 方法将层次结构放入 VisualElement 对象中。
InspectorWindow 将实例化一个包含自定义检查器的 InspectorElement 。 InspectorElement 将在自定义检查器上调用 Bind,将其绑定到 MyPlayer 对象。
<UXML xmlns="UnityEngine.UIElements" xmlns:e="UnityEditor.UIElements"> <VisualElement class="player-property"> <VisualElement class="slider-row"> <Label class="player-property-label" text="Damage"/> <VisualElement class="input-container"> <SliderInt class="player-slider" name="damage-slider" high-value="100" direction="Horizontal" binding-path="damage"/> <e:IntegerField class="player-int-field" binding-path="damage"/> </VisualElement> </VisualElement> <e:ProgressBar class="player-property-progress-bar" name="damage-progress" binding-path="damage" title="Damage"/> </VisualElement>
<VisualElement class="player-property"> <VisualElement class="slider-row"> <Label class="player-property-label" text="Armor"/> <VisualElement class="input-container"> <SliderInt class="player-slider" name="armor-slider" high-value="100" direction="Horizontal" binding-path="armor"/> <e:IntegerField class="player-int-field" binding-path="armor"/> </VisualElement> </VisualElement> <e:ProgressBar class="player-property-progress-bar" name="armor-progress" binding-path="armor" title="Armor"/> </VisualElement>
<e:PropertyField class="gun-field" binding-path="gun" label="Gun Object"/> </UXML>
当数据更改时,UIElements 会自动更新 UI,反之亦然。要绑定数据并自动更新数据和 UI,请设置“绑定路径”属性的值。
检查器的样式在 uss 中完成。
.slider-row { flex-direction: row; justify-content: space-between; margin-top: 4px; } .input-container { flex-direction: row; flex-grow: .6; margin-right: 4px; } .player-property { margin-bottom: 4px; } .player-property-label { flex:1; margin-left: 16; } .player-slider { flex:3; margin-right: 4px; } .player-property-progress-bar { margin-left: 16px; margin-right: 4px; } .player-int-field { min-width: 48px; } .gun-field { justify-content: space-between; margin-left: 16px; margin-right: 4px; margin-top: 6px; flex-grow: .6; }
这是一个使用 IMGUI 和多选的自定义检查器的示例
using UnityEditor; using UnityEngine; using System.Collections;
// Custom Editor using SerializedProperties. // Automatic handling of multi-object editing, undo, and Prefab overrides. [CustomEditor(typeof(MyPlayer))] [CanEditMultipleObjects] public class MyPlayerEditor : Editor { SerializedProperty damageProp; SerializedProperty armorProp; SerializedProperty gunProp;
void OnEnable() { // Setup the SerializedProperties. damageProp = serializedObject.FindProperty ("damage"); armorProp = serializedObject.FindProperty ("armor"); gunProp = serializedObject.FindProperty ("gun"); }
public override void OnInspectorGUI() { // Update the serializedProperty - always do this in the beginning of OnInspectorGUI. serializedObject.Update ();
// Show the custom GUI controls. EditorGUILayout.IntSlider (damageProp, 0, 100, new GUIContent ("Damage"));
// Only show the damage progress bar if all the objects have the same damage value: if (!damageProp.hasMultipleDifferentValues) ProgressBar (damageProp.intValue / 100.0f, "Damage");
EditorGUILayout.IntSlider (armorProp, 0, 100, new GUIContent ("Armor"));
// Only show the armor progress bar if all the objects have the same armor value: if (!armorProp.hasMultipleDifferentValues) ProgressBar (armorProp.intValue / 100.0f, "Armor");
EditorGUILayout.PropertyField (gunProp, new GUIContent ("Gun Object"));
// Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI. serializedObject.ApplyModifiedProperties (); }
// Custom GUILayout progress bar. void ProgressBar (float value, string label) { // Get a rect for the progress bar using the same margins as a textfield: Rect rect = GUILayoutUtility.GetRect (18, 18, "TextField"); EditorGUI.ProgressBar (rect, value, label); EditorGUILayout.Space (); } }
如果不需要自动处理多对象编辑、撤消和预制覆盖,则编辑器可以直接修改脚本变量,而无需使用 SerializedObject 和 SerializedProperty 系统,如下面的 IMGUI 示例所示。
using UnityEditor; using UnityEngine; using System.Collections;
// Example script with properties. public class MyPlayerAlternative : MonoBehaviour { public int damage; public int armor; public GameObject gun;
// ...other code... }
// Custom Editor the "old" way by modifying the script variables directly. // No handling of multi-object editing, undo, and Prefab overrides! [CustomEditor (typeof(MyPlayerAlternative))] public class MyPlayerEditorAlternative : Editor {
public override void OnInspectorGUI() { MyPlayerAlternative mp = (MyPlayerAlternative)target;
mp.damage = EditorGUILayout.IntSlider ("Damage", mp.damage, 0, 100); ProgressBar (mp.damage / 100.0f, "Damage");
mp.armor = EditorGUILayout.IntSlider ("Armor", mp.armor, 0, 100); ProgressBar (mp.armor / 100.0f, "Armor");
bool allowSceneObjects = !EditorUtility.IsPersistent (target); mp.gun = (GameObject)EditorGUILayout.ObjectField ("Gun Object", mp.gun, typeof(GameObject), allowSceneObjects); }
// Custom GUILayout progress bar. void ProgressBar (float value, string label) { // Get a rect for the progress bar using the same margins as a textfield: Rect rect = GUILayoutUtility.GetRect (18, 18, "TextField"); EditorGUI.ProgressBar (rect, value, label); EditorGUILayout.Space (); } }
hasUnsavedChanges | 此属性指定在检查器重建之前,编辑器是否提示用户保存或放弃未保存的更改。 |
saveChangesMessage | 如果提示用户保存,则向用户显示的消息。 |
serializedObject | 表示正在检查的对象或对象的 SerializedObject。 |
target | 正在检查的对象。 |
targets | 正在检查的所有对象的数组。 |
CreateInspectorGUI | 实现此方法以创建自定义 UIElements 检查器。 |
CreatePreview | 实现此方法以创建自定义 UIElements 检查器预览。 |
DiscardChanges | 放弃对编辑器内容的未保存更改。 |
DrawDefaultInspector | 绘制内置检查器。 |
DrawHeader | 调用此函数以绘制编辑器的标题。 |
DrawPreview | 预览绘制的第一个入口点。 |
GetInfoString | 实现此方法以在资源预览的顶部显示资源信息。 |
GetPreviewTitle | 如果您想更改预览区域的标签,请覆盖此方法。 |
HasPreviewGUI | 如果您实现了 OnPreviewGUI,请在子类中覆盖此方法。 |
OnInspectorGUI | 实现此函数以创建自定义检查器。 |
OnInteractivePreviewGUI | 实现以创建您自己的交互式自定义预览。交互式自定义预览用于检查器的预览区域和对象选择器。 |
OnPreviewGUI | 为检查器的预览区域、主编辑器的标题和对象选择器创建自定义预览。您必须实现 Editor.HasPreviewGUI 才能调用此方法。 |
OnPreviewSettings | 如果您想在预览标题中显示自定义控件,请覆盖此方法。 |
RenderStaticPreview | 如果您想渲染静态预览,请覆盖此方法。 |
Repaint | 重新绘制显示此编辑器的任何检查器。 |
RequiresConstantRepaint | 检查此编辑器在其当前状态下是否需要持续重新绘制。 |
SaveChanges | 对编辑器的内容执行保存操作。 |
UseDefaultMargins | 如果您不希望默认边距,请在子类中覆盖此方法以返回 false。 |
ShouldHideOpenButton | 返回检查器中“打开”按钮的可见性设置。 |
CreateCachedEditor | 返回时,previousEditor 是 targetObject 或 targetObjects 的编辑器。该函数要么返回编辑器是否已跟踪对象,要么销毁之前的编辑器并创建一个新的编辑器。 |
CreateCachedEditorWithContext | 使用上下文对象创建缓存的编辑器。 |
CreateEditor | 为 targetObject 或 targetObjects 创建自定义编辑器。 |
CreateEditorWithContext | 使用上下文对象为 targetObject 或 targetObjects 创建自定义编辑器。 |
DrawFoldoutInspector | 使用带有折叠标题的检查器 GUI 绘制 target。 |
HasFrameBounds | 验证是否可以为此编辑器计算自定义边界。 |
OnGetFrameBounds | 获取此编辑器目标的自定义边界。 |
OnSceneGUI | 使编辑器能够在场景视图中处理事件。 |
finishedDefaultHeaderGUI | 在绘制检查器窗口的标题时引发的事件,在绘制默认标题项后。 |
GetInstanceID | 获取对象的实例 ID。 |
ToString | 返回对象的名称。 |
Destroy | 移除游戏对象、组件或资源。 |
DestroyImmediate | 立即销毁对象 obj。强烈建议您改用 Destroy。 |
DontDestroyOnLoad | 加载新场景时不要销毁目标对象。 |
FindAnyObjectByType | 检索类型为 type 的任何活动加载对象。 |
FindFirstObjectByType | 检索类型为 type 的第一个活动加载对象。 |
FindObjectsByType | 检索类型为 type 的所有加载对象的列表。 |
Instantiate | 克隆对象 original 并返回克隆。 |
InstantiateAsync | 捕获原始对象(必须与某个游戏对象相关)的快照,并返回 AsyncInstantiateOperation。 |
CreateInstance | 创建可脚本化对象的实例。 |
bool | 对象是否存在? |
operator != | 比较两个对象是否引用不同的对象。 |
operator == | 比较两个对象引用以查看它们是否引用同一对象。 |