从此基类派生,以便为自定义对象创建自定义检视面板和编辑器。
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 类覆盖 Editor.CreateInspectorGUI。若要使用 IMGUI 创建自定义检视面板,必须对 Editor 类覆盖 Editor.OnInspectorGUI。如果使用 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,请为“binding-path”属性设置值。
检视面板的样式使用 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 (); } }
serializedObject | 表示正在检查的一个或多个对象的 SerializedObject。 |
target | 所检查的对象。 |
targets | 正在检查的所有对象的数组。 |
CreateInspectorGUI | 实现此方法以创建自定义 UIElements 检视面板。 |
DrawDefaultInspector | 绘制内置检视面板。 |
DrawHeader | 调用此函数以绘制编辑器标头。 |
DrawPreview | 预览绘制的第一个入口点。 |
GetInfoString | 实现此方法以在资源预览上显示资源信息。 |
GetPreviewTitle | 如果要更改 Preview 区域的标签,可重载此方法。 |
HasPreviewGUI | 如果实现 OnPreviewGUI,可在子类中重载此方法。 |
OnInspectorGUI | 实现此函数以创建自定义检视面板。 |
OnInteractivePreviewGUI | 实现此方法可创建您自己的交互式自定义预览。交互式自定义预览用于检视面板和对象选择器的预览区域。 |
OnPreviewGUI | 实现此方法可为检视面板、主编辑器标头和对象选择器的预览区域创建自己的自定义预览。 |
OnPreviewSettings | 如果要在预览标头中显示自定义控件,可重载此方法。 |
RenderStaticPreview | 如果要渲染静态预览,可重载此方法。 |
Repaint | 重新绘制显示此编辑器的任意检视面板。 |
RequiresConstantRepaint | 检查此编辑器在其当前状态下是否需要不断重绘。 |
UseDefaultMargins | 如果您不想使用默认边距,可在子类中重载此方法以返回 false。 |
ShouldHideOpenButton | 返回检视面板中“open”按钮的可见性设置。 |
CreateCachedEditor | 返回时,previousEditor 是 targetObject 或 targetObjects 的编辑器。如果该编辑器已经在跟踪对象,则函数会返回,否则该函数会销毁之前的编辑器并创建一个新编辑器。 |
CreateCachedEditorWithContext | 使用上下文对象创建缓存的编辑器。 |
CreateEditor | 为 targetObject 或 targetObjects 创建自定义编辑器。 |
CreateEditorWithContext | 使用上下文对象为 targetObject 或 targetObjects 创建自定义编辑器。 |
DrawFoldoutInspector | 为 target 绘制带有折叠标头的检视面板 GUI。 |
HasFrameBounds | Validates whether custom bounds can be calculated for this editor. |
OnGetFrameBounds | Gets custom bounds for the target of this editor. |
OnSceneGUI | 使编辑器在场景视图中处理事件。 |
finishedDefaultHeaderGUI | 在绘制默认标题项之后,在绘制 Inspector 窗口的标题时引发的事件。 |