🎯 Unity 插件开发实战:编辑器扩展核心技巧
🎯 Unity 插件开发实战:编辑器扩展核心技巧

Unity 拥有强大的编辑器扩展能力,让开发者可以为团队或自己量身打造工具。在本文中,我们将以一个“场景标注工具”的开发思路为例,总结 Unity 编辑器中的高频实用技巧,包括:
- Scene 视图右键菜单
- EditorWindow 界面分页
- Scene 中绘制图标与文字
- 支持 Undo 的删除逻辑
- 捕捉编辑器视图截图
- 导出文档与持久化配置
1️⃣ Scene 视图右键菜单的实现

Unity 不原生支持 Scene 右键菜单,但我们可以借助 SceneView.duringSceneGui 和 GenericMenu 自定义逻辑:
SceneView.duringSceneGui这是一个事件,在SceneView绘制期间每一帧都会被调用。我们通过
+=订阅了OnSceneGUI方法,这样就能在SceneView中处理GUI事件。
[InitializeOnLoad]
public static class SceneContextMenu {
static SceneContextMenu() {
SceneView.duringSceneGui += OnSceneGUI;
}
static void OnSceneGUI(SceneView view) {
if (Event.current.type == EventType.ContextClick) {
Vector2 mouse = Event.current.mousePosition;
Vector3 world = HandleUtility.GUIPointToWorldRay(mouse).origin;
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("Add Menu Here"), false, () => {
Debug.Log("Add Menu at " + world);
});
menu.ShowAsContext();
Event.current.Use();
}
}
}
2️⃣ EditorWindow 分页系统

你可以用 GUILayout.Toolbar 快速实现简易分页:
xxxxxxxxxx
int tabIndex = GUILayout.Toolbar(currentTab, new[] { "Markers", "Settings" });
switch (tabIndex) {
case 0: DrawMarkerList(); break;
case 1: DrawSettings(); break;
}
再配合:
xxxxxxxxxx
[MenuItem("Tools/Scene Marker Manager")]
public static void OpenWindow() {
GetWindow<YourWindowClass>("Marker Manager");
}
3️⃣ SceneView 中绘制 GUI 内容

标记信息如何实时显示?可使用:
xxxxxxxxxx
Handles.BeginGUI();
Vector2 guiPos = HandleUtility.WorldToGUIPoint(markerWorldPos);
GUI.Label(new Rect(guiPos.x, guiPos.y, 200, 20), "📍 Important Marker");
Handles.EndGUI();
4️⃣ 删除与 Undo 操作
支持 Undo 非常关键:
xxxxxxxxxx
Undo.RecordObject(target, "Update Marker");
target.note = "Updated";
Undo.DestroyObjectImmediate(target.gameObject);
这会让你的工具“Ctrl+Z” 友好,减少误删风险。
5️⃣ 捕捉 Scene 视图截图
如果你想保存当前 Scene 视图(不是 Game 视图),可以这么做:
xxxxxxxxxx
SceneView sv = SceneView.lastActiveSceneView;
Camera cam = sv.camera;
RenderTexture rt = new RenderTexture(1024, 768, 24);
cam.targetTexture = rt;
cam.Render();
RenderTexture.active = rt;
Texture2D tex = new Texture2D(1024, 768, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, 1024, 768), 0, 0);
tex.Apply();
byte[] bytes = tex.EncodeToPNG();
File.WriteAllBytes(path, bytes);
// 清理资源
cam.targetTexture = null;
RenderTexture.active = null;
Object.DestroyImmediate(rt);
6️⃣ 保存配置参数(EditorPrefs)
想保存设置值供下次启动使用?可用:
xxxxxxxxxx
EditorPrefs.SetFloat("SceneMarker_MaxIconSize", 36f);
float iconSize = EditorPrefs.GetFloat("SceneMarker_MaxIconSize", 28f);
适合存储像“图标大小”、“显示范围”等偏好值。
7️⃣ API详解
1. 核心结构
xxxxxxxxxx
[InitializeOnLoad]
public static class SceneContextMenu
[InitializeOnLoad]: Unity编辑器特性,在编辑器启动或脚本重新编译后自动调用静态构造函数static class: 静态类确保只存在一个实例,适合编辑器工具
2. 事件订阅
xxxxxxxxxx
SceneView.duringSceneGui += OnSceneGUI;
SceneView.duringSceneGui: Unity编辑器事件,在Scene视图每次渲染GUI时触发- 订阅模式:通过
+=添加事件处理函数
3. 事件处理
xxxxxxxxxx
if (Event.current.type == EventType.MouseDown && Event.current.button == 1)
Event.current: 当前正在处理的GUI事件EventType.MouseDown: 鼠标按下事件类型Event.current.button == 1: 检测右键点击(0=左键, 1=右键)
4. 坐标转换
xxxxxxxxxx
Vector3 worldPosition = GetWorldPosition(mousePosition, view);
xxxxxxxxxx
Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);
-
HandleUtility.GUIPointToWorldRay: 将屏幕坐标转换为世界空间中的射线 -
射线检测流程:
- 优先检测场景中的碰撞体
- 使用场景视图的基准平面作为备用
- 最后使用默认距离
5. 上下文菜单
xxxxxxxxxx
GenericMenu menu = new GenericMenu();
GenericMenu: Unity编辑器API,用于创建自定义上下文菜单
菜单项添加:
xxxxxxxxxx
menu.AddItem(new GUIContent("Create/Marker"), false, () => CreateMarker(...));
- 参数1:
GUIContent- 定义菜单项显示文本和路径(使用/创建子菜单) - 参数2:
bool isChecked- 是否显示选中标记 - 参数3:
GenericMenu.MenuFunction- 点击时的回调函数
菜单分隔符:
xxxxxxxxxx
menu.AddSeparator("Utilities/");
6. 对象创建功能
xxxxxxxxxx
GameObject.CreatePrimitive(PrimitiveType.Cube);
- 创建Unity内置原始几何体
- 支持的类型:Cube, Sphere, Cylinder, Plane等
7. 世界坐标计算
xxxxxxxxxx
Plane groundPlane = new Plane(Vector3.up, view.pivot);
- 创建XZ平面(Y轴向上)
view.pivot: 当前场景视图的基准点位置
xxxxxxxxxx
groundPlane.Raycast(ray, out distance);
- 计算射线与平面的交点
8. 编辑器操作集成
xxxxxxxxxx
Undo.RegisterCreatedObjectUndo(createdObject, undoMessage);
Undo系统:注册创建操作到Unity撤销栈- 允许用户按Ctrl+Z撤销对象创建
xxxxxxxxxx
Selection.activeObject = createdObject;
view.FrameSelected();
- 自动选择新创建的对象
- 将场景视图镜头对准选中对象
9. 自定义编辑器组件
xxxxxxxxxx
obj.AddComponent<Gizmo>();
xxxxxxxxxx
void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, 0.5f);
}
OnDrawGizmos: Unity回调方法,在场景视图中绘制辅助图形Gizmos类:提供绘制基本形状的API
xxxxxxxxxx
void OnGUI()
{
Vector3 pos = Camera.current.WorldToScreenPoint(transform.position);
GUI.Label(...);
}
OnGUI: 在场景视图中绘制2D GUIWorldToScreenPoint: 将3D世界坐标转换为屏幕坐标
✅ 总结:编辑器开发 = 自定义你的工作方式
通过上面这些实战片段你会发现,Unity 编辑器的可玩性极强。无论是团队协作、可视化调试,还是日常效率提升,学会使用编辑器扩展能力将是中高级程序员的关键跳板。
作者:世纪末的魔术师
出处:https://www.cnblogs.com/Firepad-magic/
Unity最受欢迎插件推荐:点击查看
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号