ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Unity 编辑器之 EditorTool、EditorToolBar、Overlay属性

Unity 编辑器之 EditorTool、EditorToolBar、Overlay属性 Unity面板栏EditorTool注意全局工具默认收纳在全局工具的图标内组件工具按照组件类型在全局工具图标后边依次排列全局工具示例// 放在 Assets/Editor/ 下 using UnityEngine; using UnityEditor; using UnityEditor.EditorTools; [EditorTool(My Slider Tool)] class MySliderTool : EditorTool { GUIContent m_Icon; public override GUIContent toolbarIcon { get { if (m_Icon null) m_Icon new GUIContent(, My Slider Tool); return m_Icon; } } public override void OnToolGUI(EditorWindow window) { if (Selection.transforms.Length 0) return; EditorGUI.BeginChangeCheck(); Vector3 pos Tools.handlePosition; using (new Handles.DrawingScope(Color.green)) { pos Handles.Slider(pos, Vector3.right); } if (EditorGUI.EndChangeCheck()) { Vector3 delta pos - Tools.handlePosition; Undo.RecordObjects(Selection.transforms, Slider Tool Move); foreach (var t in Selection.transforms) t.position delta; } } }组件工具示例// 运行时组件普通脚本不在 Editor 文件夹 using UnityEngine; public class PlatformPath : MonoBehaviour { public Vector3 start new Vector3(-5, 0, 0); public Vector3 end new Vector3( 5, 0, 0); }using UnityEngine; using UnityEditor; using UnityEditor.EditorTools; // ★ 第二个参数让它变成组件工具只有选中带 PlatformPath 的物体才出现 [EditorTool(Path Tool, typeof(PlatformPath))] class PathTool : EditorTool // 可选实现 IDrawSelectedHandles { public override GUIContent toolbarIcon new GUIContent(⟿, Edit Platform Path); public override void OnActivated() SceneView.lastActiveSceneView? .ShowNotification(new GUIContent(Path Tool Active), 1f); public override void OnToolGUI(EditorWindow window) { if (!(window is SceneView)) return; // targets 来自 EditorTool类型是 Object[] foreach (var o in targets) { if (!(o is PlatformPath p)) continue; EditorGUI.BeginChangeCheck(); var ws p.transform.TransformPoint(p.start); var we p.transform.TransformPoint(p.end); ws Handles.PositionHandle(ws, Quaternion.identity); we Handles.PositionHandle(we, Quaternion.identity); // 画连线便于观察 Handles.DrawLine(ws, we); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(p, Edit Path); p.start p.transform.InverseTransformPoint(ws); p.end p.transform.InverseTransformPoint(we); EditorUtility.SetDirty(p); } } } }代码中需要注意的点OnToolGui 默认是在3D空间使用世界坐标绘制如果想切换2D空间需要使用Handles.BeginGui/EndGui 开启关闭2D空间TransformPoint 把坐标转换成世界坐标InverseTransformPoint 把世界坐标转换成局部坐标Handles.DrawingScope这是一个“状态管理器”。当创建一个它的实例时它会保存当前的 Gizmos/Handles 绘制状态包括颜色 Handles.color、矩阵 Handles.matrix 等。using ( ... ) { ... }这是 C# 的语法。当代码执行进入 { } 时DrawingScope 生效当代码执行离开 { }即使是因为报错跳出Dispose() 方法会被自动调用DrawingScope 会把之前保存的状态恢复回去。EditorToolBar这四个类和EditorTool类似都是防止在Scene工具栏中但是可以自定义浮动栏一般和overlay配合使用继承自ToolBarOverlay一次性添加(浮动条)using UnityEditor; using UnityEditor.Overlays; using UnityEditor.Toolbars; using UnityEngine; [EditorToolbarElement(CustomEditorToolbarButton, typeof(SceneView))] public class CustomEditorToolbarButton : EditorToolbarButton { public const string ID CustomEditorToolbarButton; public CustomEditorToolbarButton() { text Custom Editor Toolbar Button; clickable.clicked () Debug.Log(Custom Editor Toolbar Button Clicked!); } } [Overlay(typeof(SceneView), EditorToolBarOverlayExample, true)] public class EditorToolBarOverlayExample : ToolbarOverlay { public EditorToolBarOverlayExample() : base(CustomEditorToolbarButton.ID) { } }使用Overlay属性主动添加(面板)using UnityEditor; using UnityEditor.Overlays; using UnityEditor.Toolbars; using UnityEngine; using UnityEngine.UIElements; [Overlay(typeof(SceneView), EditorToolBarExample, true)] public class EditorToolBarExample : Overlay { public override VisualElement CreatePanelContent() { base.CreatePanelContent(); var root new VisualElement(); // 添加button root.Add(new EditorToolbarButton(() { Debug.Log(Editor Toolbar Button Clicked!); })); // 添加dropdown var dropdownButton new EditorToolbarDropdown(() { Debug.Log(Editor Toolbar Dropdown Clicked!); }); dropdownButton.text Dropdown; dropdownButton.icon EditorGUIUtility.IconContent(d_winbtn_win_max).image as Texture2D; dropdownButton.clicked () { GenericMenu menu new GenericMenu(); menu.AddItem(new GUIContent(Option 1), false, () { Debug.Log(Option 1 Selected); }); menu.AddItem(new GUIContent(Option 2), false, () { Debug.Log(Option 2 Selected); }); menu.AddItem(new GUIContent(Option 3), false, () { Debug.Log(Option 3 Selected); }); menu.DropDown(dropdownButton.worldBound); }; root.Add(dropdownButton); // 添加toggle var editorToolbarToggle new EditorToolbarToggle(); editorToolbarToggle.text Toggle; editorToolbarToggle.icon EditorGUIUtility.IconContent(d_winbtn_win_max).image as Texture2D; editorToolbarToggle.RegisterValueChangedCallback(evt { Debug.Log($Editor Toolbar Toggle Value Changed: {evt.newValue}); }); root.Add(editorToolbarToggle); return root; } }EditorTool 与 Overlay的示例Unity 官方在Overlay脚本参考里给出了一个教科书级示例——核心思路是EditorTool 内部嵌套定义一个 Overlay 子类然后在工具的OnActivated/OnWillBeDeactivated里控制这个 Overlay 的显示与移除。主要方法SceneView.AddOverlayToActiveView(m_Overlay)SceneView.RomoveOverlayToActiveView(m_Overlay)using System.Linq; using UnityEngine; using UnityEditor; using UnityEditor.EditorTools; using UnityEditor.Overlays; [EditorTool(Offset, typeof(Transform))] public class OffsetTool : EditorTool { // ★ Overlay 作为 EditorTool 的内部类 // defaultDisplay true第一次被加到画布时就可见 [Overlay(defaultDisplay true)] class OffsetToolOverlay : Overlay { Transform[] selection; public OffsetToolOverlay(Transform[] targets) selection targets; public override VisualElement CreatePanelContent() { var root new VisualElement(); root.Add(new Label(Offset Tool Settings)); var field new TextField(Offset); field.RegisterValueChangedCallback(evt { // 处理用户输入的逻辑 }); root.Add(field); return root; } } OffsetToolOverlay m_Overlay; // ★ 工具被激活时 public override void OnActivated() { m_Overlay new OffsetToolOverlay(targets.CastTransform().ToArray()); // ★★ 关键 API把 Overlay 加到当前激活的 SceneView 上 SceneView.AddOverlayToActiveView(m_Overlay); } // ★ 工具被停用时 public override void OnWillBeDeactivated() { // ★★ 关键 API从 SceneView 移除 Overlay if (m_Overlay ! null) SceneView.RemoveOverlayFromActiveView(m_Overlay); } // 3D 手柄绘制 public override void OnToolGUI(EditorWindow window) { if (!(window is SceneView)) return; // Handles.Slider(...) 等 } }QAQ除了SceneWindow还有别的View可以使用吗A目前只支持SceneWindow以及实现了ISupportsOverlays接口的自定义EditorWindowusing UnityEditor; using UnityEditor.Overlays; using UnityEngine.UIElements; public class MyOverlayWindow : EditorWindow, ISupportsOverlays { [MenuItem(Window/My Overlay Window)] public static void ShowWindow() GetWindowMyOverlayWindow(); // 实现 ISupportsOverlays 接口 // 该接口是 marker interface不需要实现任何方法 } // 然后就可以把 Overlay 注册到这个窗口了 [Overlay(typeof(MyOverlayWindow), My Panel)] public class MyOverlay : Overlay { public override VisualElement CreatePanelContent() { return new Label(Hello from MyOverlayWindow!); } }QHandles类都有什么方法还有别的类用来在SceneWindow中操作吗AHandles类负责绘画3D相关的图形使用世界坐标除了它还有HandleUtility\Tool类辅助
返回列表