【unity游戏开发——编辑器扩展】Scene窗口拓展
注意
:考虑到编辑器扩展的内容比较多,我将编辑器扩展的内容分开,并全部整合放在【unity游戏开发——编辑器扩展】专栏里,感兴趣的小伙伴可以前往逐一查看学习。
文章目录
- 前言
- 一、Scene窗口更新响应函数
- 1、步骤
- 2、实战
- 2.1 声明自定义脚本
- 2.2 声明继承Editor的编辑器脚本
- 2.3 效果
- 二、自定义窗口中监听Scene窗口更新响应函数
- 1、实现步骤
- 2、实战
- 三、设置文本、线段、虚线
- 四、绘制弧线、圆、立方体、几何体
- 1、绘制弧线(圆弧)
- 1.1 介绍
- 1.2 实战
- 2、绘制圆
- 2.1 介绍
- 2.2 实战
- 3、绘制立方体线框
- 3.1 介绍
- 3.2 实战
- 4、Handles中的几何体
- 3.1 介绍
- 3.2 实战
- 五、常显示移动、旋转、缩放坐标轴
- 1、移动轴
- 1.1 介绍
- 1.2 实战
- 2、旋转轴
- 2.1 介绍
- 2.2 实战
- 3、缩放轴
- 2.1 介绍
- 2.2 实战
- 六、自由移动、自由旋转辅助控制柄
- 1、自由移动辅助控制柄
- 1.1 介绍
- 1.2 实战
- 2、自由旋转辅助控制柄
- 2.1 介绍
- 2.2 实战
- 七、Scene中显示GUI
- 1、Scene中简单的显示GUI控件
- 1.1 介绍
- 1.2 实战
- 2、精确设置GUI控件在Scene窗口显示的位置
- 2.1 介绍
- (1) 获取Scene窗口大小
- (2) 自动布局 GUI 界面
- 2.2 实战
- 八、HandleUtility公共工具类
- 1、获取合适的手柄尺寸 - GetHandleSize
- 1.1 介绍:
- 1.2 应用场景:
- 2、世界坐标转屏幕坐标 - WorldToGUIPoint
- 2.1 介绍:
- 2.2 应用场景:
- 3、屏幕坐标转世界射线 - GUIPointToWorldRay
- 3.1 介绍:
- 3.2 应用场景:
- 4、计算到线段的距离 - DistanceToLine
- 4.1 介绍:
- 4.2 应用场景:
- 5、拾取游戏对象 - PickGameObject
- 5.1 介绍:
- 5.2 应用场景:
- 专栏推荐
- 完结
前言
Handles类提供了很多API,让我们可以在Scene窗口中绘制我们的自定义内容。它和GUI、EditorGUI类似,只不过它专门提供给Scene窗口使用。
想要在Scene窗口中显示自定义内容,我们需要在对应的响应函数中进行处理。
一、Scene窗口更新响应函数
1、步骤
实现步骤和自定义Inspector窗口显示内容基本一致:
-
单独为某一个脚本实现一个自定义脚本,并且脚本需要继承Editor,一般该脚本命名为
自定义脚本名 + Editor
。 -
在该脚本前加上特性,命名空间:UnityEditor,特性名:
CustomEditor(想要自定义脚本类名的Type)
。 -
在该脚本中实现
OnSceneGUI
方法,该方法会在我们选中挂载自定义脚本的对象时自动更新。注意:只有选中时才会执行,没有选中不执行。
2、实战
2.1 声明自定义脚本
using UnityEngine;public class TestSceneMono : MonoBehaviour
{}
2.2 声明继承Editor的编辑器脚本
脚本加上特性并实现OnSceneGUI方法
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");}
}
2.3 效果
选中挂载TestSceneMono脚本的对象时打印
二、自定义窗口中监听Scene窗口更新响应函数
1、实现步骤
- 可以在自定义窗口显示时监听更新事件:
SceneView.duringSceneGui += 事件函数
- 窗口隐藏或销毁时移除事件:
SceneView.duringSceneGui -= 事件函数
2、实战
using UnityEditor;
using UnityEngine;public class TestSceneMonoWindow : EditorWindow
{[MenuItem("编辑器拓展/自定义窗口拓展/Scene窗口拓展")]private static void OpenWindow(){TestSceneMonoWindow win = EditorWindow.GetWindow<TestSceneMonoWindow>();win.Show();}private void OnEnable(){SceneView.duringSceneGui += SceneUpdate;}private void OnDestroy(){SceneView.duringSceneGui -= SceneUpdate;}//监听Scene窗口更新响应函数private void SceneUpdate(SceneView view){Debug.Log("Scene窗口更新");}
}
效果
三、设置文本、线段、虚线
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");//注意:文本是不受绘制颜色控制的,要自己设置GUIstyleGUIStyle style = new GUIStyle();style.normal.textColor = Color.blue;// 文本 参数1:文本位置 参数2:文本内容Handles.Label(testSceneMono.transform.position, "测试文本显示", style);// 颜色Handles.color = Color.red;// 绘制线段: 参数1:起点 参数2:终点 参数3:线条粗细Handles.DrawLine(testSceneMono.transform.position, testSceneMono.transform.position + testSceneMono.transform.right * 5, 5);//绘制虚线: 参数1:起点 参数2:终点 参数3:线条粗细Handles.DrawDottedLine(testSceneMono.transform.position, testSceneMono.transform.position + testSceneMono.transform.forward * 5, 5);}
}
效果
四、绘制弧线、圆、立方体、几何体
1、绘制弧线(圆弧)
1.1 介绍
- 绘制线框弧线
Handles.DrawWireArc(圆心, 法线, 绘制朝向, 角度, 半径);
- 绘制填充弧线
Handles.DrawSolidArc(圆心, 法线, 绘制朝向, 角度, 半径);
1.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");// 颜色Handles.color = Color.red;// 如果向圆弧跟着对象动,第二个参数传入本地坐标,例如testSceneMono.transform.up,Handles.DrawWireArc(testSceneMono.transform.position, Vector3.up, testSceneMono.transform.forward, 30, 5);// 向左旋转15度 这样以人物正前方为等分Handles.DrawSolidArc(testSceneMono.transform.position, testSceneMono.transform.up, Quaternion.Euler(0,-15,0)* testSceneMono.transform.forward, 30, 4);}
}
效果
2、绘制圆
2.1 介绍
绘制填充圆
Handles.DrawSolidDisc(圆心, 法线, 半径);
绘制线框圆
Handles.DrawWireDisc(圆心, 法线, 半径);
2.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");// 颜色Handles.color = Color.red;Handles.DrawSolidDisc(testSceneMono.transform.position, testSceneMono.transform.up, 2);Handles.DrawWireDisc(testSceneMono.transform.position, testSceneMono.transform.up, 3);}
}
效果
3、绘制立方体线框
3.1 介绍
主要API
Handles.DrawWireCube(中心点, xyz大小);
3.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");// 颜色Handles.color = Color.red;Handles.DrawWireCube(testSceneMono.transform.position, Vector3.one);}
}
效果
4、Handles中的几何体
3.1 介绍
主要API
Handles.DrawAAConvexPolygon(几何体各顶点);
3.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");// 颜色Handles.color = Color.red;// 在原点绘制一个四边形Handles.DrawAAConvexPolygon(Vector3.zero, Vector3.right, Vector3.right + Vector3.forward, Vector3.forward);}
}
效果
五、常显示移动、旋转、缩放坐标轴
1、移动轴
1.1 介绍
可以在选择其他默认不显示移动坐标轴工具栏也显示移动轴,以下两个API作用一致
Vector3 Handles.DoPositionHandle(位置, 角度);
Vector3 Handles.PositionHandle(位置, 角度);
注意:假如传入Vector.zero
或者Quaternion.identity
这种写死的全局值,移动轴和对象固定在全局的位置上,且无法移动对象位置了。
1.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");// 常显示移动轴testSceneMono.transform.position = Handles.DoPositionHandle(testSceneMono.transform.position, testSceneMono.transform.rotation);// testSceneMono.transform.position = Handles.PositionHandle(testSceneMono.transform.position, testSceneMono.transform.rotation);}
}
效果
2、旋转轴
2.1 介绍
可以在选择其他默认不显示旋转坐标轴工具栏也显示旋转轴,以下两个API作用一致
Quaternion Handles.DoRotationHandle(角度, 位置);
Quaternion Handles.RotationHandle(角度, 位置);
2.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");// 常显示旋转轴testSceneMono.transform.rotation = Handles.DoRotationHandle(testSceneMono.transform.rotation, testSceneMono.transform.position);// testSceneMono.transform.rotation = Handles.RotationHandle(testSceneMono.transform.rotation, testSceneMono.transform.position);}
}
效果
3、缩放轴
2.1 介绍
可以在选择其他默认不显示缩放坐标轴工具栏也显示缩放轴,以下两个API作用一致
Vector3 Handles.DoScaleHandle(缩放, 位置, 角度, HandleUtility.GetHandleSize(位置));
Vector3 Handles.ScaleHandle(缩放, 位置, 角度, HandleUtility.GetHandleSize(位置));
HandleUtility.GetHandleSize方法的作用是
- 获取给定位置的操纵器控制柄的世界空间大小
- 使用当前相机计算合适的大小
- 它决定了控制柄的缩放大小
- 最后一个参数的括号中传入Vector3.zero的话,缩放轴不会变化,传入testSceneMono.transform.position缩放轴长短会随对象位置变化
2.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Debug.Log("选中挂载TestSceneMono的对象");// 常显缩放轴testSceneMono.transform.localScale = Handles.DoScaleHandle(testSceneMono.transform.localScale, testSceneMono.transform.position, testSceneMono.transform.rotation,HandleUtility.GetHandleSize(testSceneMono.transform.position));testSceneMono.transform.localScale = Handles.ScaleHandle(testSceneMono.transform.localScale, testSceneMono.transform.position, testSceneMono.transform.rotation,HandleUtility.GetHandleSize(Vector3.zero));}
}
效果
六、自由移动、自由旋转辅助控制柄
1、自由移动辅助控制柄
1.1 介绍
主要API
Vector3 Handles.FreeMoveHandle(位置, 句柄大小, 移动步进值(按住ctrl键时会按该单位移动), 渲染控制手柄的回调函数);
- 渲染控制手柄的常用回调函数:
Handles.RectangleHandleCap
: 一个矩形形状的控制手柄,通常用于表示一个平面的控制面Handles.CircleHandleCap
: 一个圆形的控制手柄,通常用于表示一个球体的控制面Handles.ArrowHandleCap
: 一个箭头形状的控制手柄,通常用于表示方向
1.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){// 自由移动testSceneMono.transform.position = Handles.FreeMoveHandle(testSceneMono.transform.position,HandleUtility.GetHandleSize(testSceneMono.transform.position),Vector3.one * 5,Handles.RectangleHandleCap);}
}
结果,会始终绘制一个矩形对着摄像机,鼠标按下在矩形范围内可以自由移动对象
2、自由旋转辅助控制柄
2.1 介绍
主要API
Quaternion Handles.FreeRotateHandle(角度, 位置, 句柄大小);
2.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){// 自由旋转testSceneMono.transform.rotation = Handles.FreeRotateHandle(testSceneMono.transform.rotation,Vector3.zero,HandleUtility.GetHandleSize(Vector3.zero));}
}
结果,会始终绘制一个写死在(0,0)位置的圆形对着摄像机,鼠标按下在圆形范围内可以自由旋转对象
七、Scene中显示GUI
1、Scene中简单的显示GUI控件
1.1 介绍
GUI代码放在Handles.BeginGUI();
和Handles.EndGUI();
之间即可。
Handles.BeginGUI();
// GUI相关代码
Handles.EndGUI();
1.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Handles.BeginGUI();if(GUILayout.Button("测试按钮")){Debug.Log("点击打印");}Handles.EndGUI();}
}
效果
点击按钮打印
2、精确设置GUI控件在Scene窗口显示的位置
2.1 介绍
(1) 获取Scene窗口大小
我们可以使用SceneView.currentDrawingSceneView
获取当前Scene窗口信息。它继承自EditorWindow,因此通过position就能得到它的大小。
float w = SceneView.currentDrawingSceneView.position.width;
float h = SceneView.currentDrawingSceneView.position.height;
得到宽高可以精确设置需要显示的控件在Scene窗口的哪里。
(2) 自动布局 GUI 界面
GUILayout.BeginArea
是 Unity 引擎中用于 GUI 布局的一个方法,它允许你在屏幕的特定区域定义一个自动布局的 GUI 块。必须与 GUILayout.EndArea() 配对使用
GUILayout.BeginArea(new Rect(x, y, width, height));
// 在这里放置你的GUI元素
GUILayout.EndArea();
比如
// 定义一个200x100的区域,距离屏幕左上角(50,50)
GUILayout.BeginArea(new Rect(50, 50, 200, 100));// 区域内的GUI元素
GUILayout.Label("这是一个标签");
if (GUILayout.Button("点击我")) {Debug.Log("按钮被点击");
}// 结束区域
GUILayout.EndArea();
2.2 实战
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{//选中挂载自定义脚本的对象时调用// 在场景视图中绘制GUIprivate void OnSceneGUI(){// 开始绘制GUIHandles.BeginGUI();// 获取当前场景视图的宽度和高度float w = SceneView.currentDrawingSceneView.position.width;float h = SceneView.currentDrawingSceneView.position.height;// 在场景视图的右下角绘制一个100x100的区域GUILayout.BeginArea(new Rect(w - 100, h - 100, 100, 100));// 在区域中显示一个标签GUILayout.Label("测试文本控件显示");// 在区域中显示一个按钮if (GUILayout.Button("测试按钮")){// 当按钮被点击时,打印一条日志Debug.Log("点击打印");}// 结束绘制GUIGUILayout.EndArea();// 结束绘制GUIHandles.EndGUI();}
}
效果
八、HandleUtility公共工具类
HandleUtility是Unity编辑器中的一个实用工具类,专门用于处理场景视图(Scene View
)中的交互操作。它提供了许多方便的方法来帮助开发者创建自定义编辑器工具和场景交互功能。
1、获取合适的手柄尺寸 - GetHandleSize
1.1 介绍:
根据物体距离摄像机的远近,自动计算出一个合适的手柄显示大小,这样无论物体远近,手柄都不会显得过大或过小。
1.2 应用场景:
当你自定义场景中的Gizmo或手柄时,可以用这个方法让它们在不同距离下都有良好的显示效果。
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){// 获取当前位置合适的手柄大小float handleSize = HandleUtility.GetHandleSize(testSceneMono.transform.position);// 绘制一个圆形手柄,大小会自动适应Handles.CircleHandleCap(0, testSceneMono.transform.position,testSceneMono.transform.rotation,handleSize * 0.5f,EventType.Repaint);}
}
效果
2、世界坐标转屏幕坐标 - WorldToGUIPoint
2.1 介绍:
把3D场景中的物体位置转换成2D屏幕上的像素坐标,方便在对应位置绘制GUI元素。
2.2 应用场景:
在物体旁边显示标签、按钮等UI元素。
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){// 把物体位置转换为屏幕坐标Vector2 screenPos = HandleUtility.WorldToGUIPoint(testSceneMono.transform.position);Handles.BeginGUI();// 在物体位置上方绘制一个跟随的按钮GUI.Button(new Rect(screenPos.x - 50, screenPos.y - 30, 100, 20), "点击我");Handles.EndGUI();}
}
效果
3、屏幕坐标转世界射线 - GUIPointToWorldRay
3.1 介绍:
把鼠标在屏幕上的点击位置转换成一条从摄像机发射的3D射线,用于检测鼠标指向的物体。
3.2 应用场景:
实现自定义物体选择、射线检测等交互功能。
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){// 获取当前鼠标位置对应的3D射线Ray mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);// 进行射线检测if (Physics.Raycast(mouseRay, out RaycastHit hit)){// 鼠标悬停的立方体物体,绘制一个大小一样的红色的立方体线框Handles.color = Color.red;Handles.DrawWireCube(hit.collider.bounds.center, hit.collider.bounds.size);}}
}
效果
4、计算到线段的距离 - DistanceToLine
4.1 介绍:
计算鼠标位置到场景中某条线段的最短距离,可以用来判断鼠标是否靠近这条线。
4.2 应用场景:
实现线段悬停高亮效果、自定义路径编辑工具等。
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{private TestSceneMono testSceneMono;private void OnEnable(){testSceneMono = target as TestSceneMono;//获取当前拓展的组件对象}//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){Vector3 start = testSceneMono.transform.position;Vector3 end = start + Vector3.right * 3;// 计算鼠标到线段的距离float distance = HandleUtility.DistanceToLine(start, end);// 根据距离改变线段颜色Handles.color = distance < 0.5f ? Color.green : Color.white;Handles.DrawLine(start, end);}
}
效果
5、拾取游戏对象 - PickGameObject
5.1 介绍:
模拟Unity编辑器的对象选择功能,通过鼠标位置获取场景中的游戏对象。
5.2 应用场景:
实现自定义的选择逻辑或扩展编辑器选择功能。
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(TestSceneMono))]
public class TestSceneMonoEditor : Editor
{//选中挂载自定义脚本的对象时调用private void OnSceneGUI(){// 检测鼠标左键点击if (Event.current.type == EventType.MouseDown && Event.current.button == 0){// 拾取鼠标位置下的游戏对象GameObject selectedObj = HandleUtility.PickGameObject(Event.current.mousePosition, true);if (selectedObj != null){Debug.Log($"选中了: {selectedObj.name}");// 这里可以添加自定义的选择处理逻辑}}}
}
效果
专栏推荐
地址 |
---|
【unity游戏开发入门到精通——C#篇】 |
【unity游戏开发入门到精通——unity通用篇】 |
【unity游戏开发入门到精通——unity3D篇】 |
【unity游戏开发入门到精通——unity2D篇】 |
【unity实战】 |
【制作100个Unity游戏】 |
【推荐100个unity插件】 |
【实现100个unity特效】 |
【unity框架/工具集开发】 |
【unity游戏开发——模型篇】 |
【unity游戏开发——InputSystem】 |
【unity游戏开发——Animator动画】 |
【unity游戏开发——UGUI】 |
【unity游戏开发——联网篇】 |
【unity游戏开发——优化篇】 |
【unity游戏开发——shader篇】 |
【unity游戏开发——编辑器扩展】 |
完结
好了,我是向宇
,博客地址:https://xiangyu.blog.csdn.net,如果学习过程中遇到任何问题,也欢迎你评论私信找我。
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!