- ⚠️ Editor文件夹
- 拓展Project面板
- 拓展菜单
- 效果
- 注释
- 源码
- 拓展布局
- 效果
- 源码
- 监听事件
- 拓展Hierarchy面板
- 拓展菜单
- 效果
- 源码
- 修改编辑器默认的Image游戏对象
- 效果
- 源码
- 拓展布局
- 效果
- 源码
- 拓展Inspector面板
- 拓展面板视图(布局)
- 效果
- 源码
- 针对Unity内部已经重绘过的组件,拓展面板视图(布局)并保留Unity内部原有的绘制
- `不保留Unity内部原有的绘制` 效果
- `保留Unity内部原有的绘制` 效果
- 源码
- 禁用组件
- 效果
- 源码
- 批量启用/禁用所选游戏对象的组件
- 效果
- 源码
- 拓展菜单
- 效果
- 源码
- 拓展菜单(获取当前游戏对象绑定的脚本)
- 绑定的脚本
- 效果
- 源码
拓展Project面板 拓展菜单 效果Editor可以为任何文件夹的子文件夹
Editor下的文件为Unity编辑模式下的文件,非编辑模式下的脚本文件放在非Editor文件夹下即可
源码需要在指定目录下拓展菜单可配置成如下示例:
[MenuItem("Assets/xx/xxxx", false, 1)](将在Assets菜单下创建xx/xxxx菜单)
// 参数1: 菜单位置; 参数3: 菜单排序(数值越小越靠前);
[MenuItem("自定义工具箱/打印/打印当前游戏对象名称", false, 1)]
private static void Log1()
{
if (Selection.activeObject != null)
Debug.Log($"当前选择的游戏对象 ---> {Selection.activeObject.name}");
}
[MenuItem("自定义工具箱/打印/打印 Hello World", false, 0)]
private static void Log2()
{
Debug.Log("Hello World");
}
[MenuItem("自定义工具箱/创建/立方体", false, 0)]
private static void CreateCube()
{
// 创建立方体
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
// 修改游戏对象名称
cube.name = "立方体";
// 添加刚体组件
cube.AddComponent();
}
[MenuItem("自定义工具箱/创建/球体", false, 1)]
private static void CreateSphere()
{
GameObject.CreatePrimitive(PrimitiveType.Sphere);
}
拓展布局
效果
源码
// InitializeOnLoadMethod 确保编译后首先调用此方法
[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod01()
{
// 监听EditorApplication.projectWindowItemOnGUI委托,即可使用GUI来绘制UI元素
EditorApplication.projectWindowItemOnGUI = delegate(string guid, Rect rect)
{
// 判断是否选中(向选中的元素添加UI元素)
if (Selection.activeObject &&
guid == AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject)))
{
const float width = 90f;
rect.x += rect.width - width;
rect.width = width;
// 创建按钮
var isClick = GUI.Button(rect, "打印当前对象");
// 判断是否点击
if (isClick)
Debug.Log($"当前点击的游戏对象 ---> {Selection.activeObject.name}");
}
};
}
监听事件
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
// 监听事件
namespace Scripts_03.Editor
{
public class WatchMethod : UnityEditor.AssetModificationProcessor
{
[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod()
{
// 全局监听Project视图下的资源是否发生变化(添加、删除、移动)
EditorApplication.projectChanged += delegate { Debug.Log("Changed"); };
}
// 监听 资源点击、打开、修改 事件
private static bool IsOpenForEdit(string[] paths, List outNotEditablePaths,
StatusQueryOptions statusQueryOptions)
{
Debug.Log($"资源点击、打开、修改 ---> {string.Join(",", paths)}");
// true允许在Unity中打开资源; false不允许在Unity中打开资源
return false;
}
// 监听 资源即将被创建 事件
private static void OnWillCreateAsset(string assetName)
{
Debug.Log($"资源即将被创建 ---> {assetName}");
}
// 监听 资源即将被保存 事件
private static string[] OnWillSaveAssets(string[] paths)
{
Debug.Log($"资源即将被保存 ---> {string.Join(",", paths)}");
return paths;
}
// 监听 资源即将被移动 事件
private static AssetMoveResult OnWillMoveAsset(string sourcePath, string destinationPath)
{
Debug.Log($"资源即将被移动 ---> {sourcePath} ===> {destinationPath}");
// DidNotMove可移动 DidMove不可移动 FailedMove错误移动
return AssetMoveResult.DidNotMove;
}
// 监听 资源即将被删除 事件
private static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options)
{
Debug.Log($"资源即将被删除 ---> {assetPath}");
// DidNotDelete可删除 DidDelete不可删除 FailedDelete错误删除
return AssetDeleteResult.DidNotDelete;
}
}
}
拓展Hierarchy面板
拓展菜单
效果
源码
[MenuItem("Window/Test/菜单 1")]
private static void TestMenu01()
{
Debug.Log("TestMenu01");
}
[MenuItem("Window/Test/菜单 2")]
private static void TestMenu02()
{
Debug.Log("TestMenu02");
}
[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod01()
{
EditorApplication.hierarchyWindowItemOnGUI = delegate(int id, Rect rect)
{
// 修改菜单 右键显示
// rect.Contains(Event.current.mousePosition) 点击在游戏对象上
// Event.current.button == 1 右键
// Event.current.type EventType.MouseDown || EventType.MouseUp
if (Event.current is { button: 1 } && rect.Contains(Event.current.mousePosition) &&
Event.current.type <= EventType.MouseUp)
{
// 当前点击的游戏对象
GameObject selectedGameObject = EditorUtility.InstanceIDToObject(id) as GameObject;
// 当游戏对象名称改为NewGameObject时,使用重写的菜单
if (selectedGameObject!.name == "NewGameObject")
{
// 鼠标点击位置
Vector2 mousePosition = Event.current.mousePosition;
Rect position = new Rect(mousePosition.x, mousePosition.y, 0, 0);
// 弹出菜单
EditorUtility.DisplayPopupMenu(position, "Window/Test", null);
// 不再执行原有的操作
Event.current.Use();
}
}
};
}
修改编辑器默认的Image游戏对象
效果
[MenuItem("GameObject/UI/Image")]
private static void CreateCustomImage()
{
if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent
拓展布局
效果
源码
[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod01()
{
EditorApplication.hierarchyWindowItemOnGUI = delegate(int id, Rect rect)
{
// 拓展布局 选中后显示
if (Selection.activeObject && id == Selection.activeObject.GetInstanceID())
{
const float width = 90f;
rect.x += rect.width - width;
rect.width = width;
// 创建按钮
var isClick = GUI.Button(rect, "打印当前对象");
// 判断是否点击
if (isClick)
Debug.Log($"当前点击的游戏对象 ---> {Selection.activeObject.name}");
}
};
}
拓展Inspector面板
拓展面板视图(布局)
效果
源码
[CustomEditor(typeof(Camera))]
public class ExpandInspector1 : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
if (GUILayout.Button("拓展按钮"))
Debug.Log("拓展按钮");
base.OnInspectorGUI();
}
}
针对Unity内部已经重绘过的组件,拓展面板视图(布局)并保留Unity内部原有的绘制
不保留Unity内部原有的绘制 效果 保留Unity内部原有的绘制 效果 源码针对Unity内部已经重写了绘制方法的组件,内部的方法未公开,可以采用C#反射的方式调用内部未公开的方法来保留Unity对组件原有的绘制方式
[CustomEditor(typeof(Transform))]
public class ExpandInspector2 : UnityEditor.Editor
{
// 针对Unity内部已经重写了绘制方法的组件,内部的方法未公开,可以采用C#反射的方式调用内部未公开的方法来保留Unity对组件原有的绘制方式
private UnityEditor.Editor _editor;
private void OnEnable()
{
// 通过反射获取到UnityEditor.TransformInspector对象
_editor = CreateEditor(target,
Assembly.GetAssembly(typeof(UnityEditor.Editor)).GetType("UnityEditor.TransformInspector", true));
}
public override void OnInspectorGUI()
{
if (GUILayout.Button("未被禁用的拓展按钮"))
Debug.Log("未被禁用的拓展按钮");
// 调用系统绘制方法
_editor.OnInspectorGUI();
}
}
禁用组件
效果
源码
[CustomEditor(typeof(Transform))]
public class ExpandInspector2 : UnityEditor.Editor
{
// 针对Unity内部已经重写了绘制方法的组件,内部的方法未公开,可以采用C#反射的方式调用内部未公开的方法来保留Unity对组件原有的绘制方式
private UnityEditor.Editor _editor;
private void OnEnable()
{
// 通过反射获取到UnityEditor.TransformInspector对象
_editor = CreateEditor(target,
Assembly.GetAssembly(typeof(UnityEditor.Editor)).GetType("UnityEditor.TransformInspector", true));
}
public override void OnInspectorGUI()
{
if (GUILayout.Button("未被禁用的拓展按钮"))
Debug.Log("未被禁用的拓展按钮");
// 开始禁用(被包括范围内的控件将被禁用)
GUI.enabled = false;
// 调用系统绘制方法
_editor.OnInspectorGUI();
GUILayout.Button("被禁用的拓展按钮");
// 结束禁用(被包括范围内的控件将被禁用)
GUI.enabled = true;
}
}
批量启用/禁用所选游戏对象的组件
效果
源码
// 启用禁用游戏对象组件
public static class ExpandInspector3
{
[MenuItem("GameObject/3D Object/启用-禁用/禁用所选游戏对象的所有组件")]
private static void Lock()
{
if (Selection.gameObjects != null)
{
foreach (GameObject go in Selection.gameObjects)
{
go.hideFlags = HideFlags.NotEditable;
}
}
}
[MenuItem("GameObject/3D Object/启用-禁用/启用所选游戏对象的所有组件")]
private static void UnLock()
{
if (Selection.gameObjects != null)
{
foreach (GameObject go in Selection.gameObjects)
{
go.hideFlags = HideFlags.None;
}
}
}
}
拓展菜单
效果
// 配置Inspector的Context菜单
public static class ExpandInspector4
{
[MenuItem("CONTEXT/Transform/菜单 01")]
private static void Context01(MenuCommand menuCommand)
{
// 获取游戏对象名
Debug.Log(menuCommand.context.name);
}
[MenuItem("CONTEXT/Light/菜单 02")]
private static void Context02(MenuCommand menuCommand)
{
// 获取游戏对象名
Debug.Log(menuCommand.context.name);
}
}
拓展菜单(获取当前游戏对象绑定的脚本)
绑定的脚本
using UnityEngine;
namespace Scripts_03
{
public class CapsuleScript : MonoBehaviour
{
public string msg = "Hello World!";
private const string Msg = "Hello Lee!";
public void SayHello()
{
Debug.Log($"{msg} --- {Msg} --- Hello");
}
}
}
效果
internal class ExpandInspector5 : MonoBehaviour
{
#if UNITY_EDITOR
// UNITY_EDITOR 只会在Editor模式下执行,发布后将被剔除掉
// 通过菜单获取脚本内变量
[MenuItem("CONTEXT/CapsuleScript/菜单 03")]
private static void ScriptContext(MenuCommand menuCommand)
{
CapsuleScript capsuleScript = menuCommand.context as CapsuleScript;
capsuleScript!.SayHello();
// Debug可以查看脚本中的变量
Debug.Log(capsuleScript);
}
// 覆盖原有菜单
[MenuItem("CONTEXT/CapsuleScript/Remove Component")]
private static void RemoveComponent(MenuCommand menuCommand)
{
Debug.Log("RemoveComponent");
CapsuleScript capsuleScript = menuCommand.context as CapsuleScript;
// 等一帧在删除自己
EditorApplication.delayCall = delegate { DestroyImmediate(capsuleScript); };
}
#endif
}



