1. Unity编辑器扩展入门:编辑器扩展的概念、应用场景、第一个Editor脚本

大家好,欢迎来到这门课的第一讲。

说实话,我刚开始用Unity那会儿,压根没想过还能自己改编辑器。那时候就觉得——编辑器嘛,官方给啥我用啥。直到有一次,项目里要批量处理几百个预制体,手动操作到凌晨三点...嗯,从那以后,我彻底入了编辑器扩展的坑。

什么是编辑器扩展?

说白了,编辑器扩展就是让你能修改Unity编辑器本身的行为。你可以加按钮、加窗口、加菜单,甚至改写Inspector面板的显示方式。

我个人的理解是:编辑器扩展 = 给Unity编辑器写插件。只不过这个插件是给开发者自己用的,不是给玩家用的。

核心概念:编辑器扩展运行在Editor模式下,不会被打包进游戏。它只影响开发阶段的工作流。

应用场景:什么时候需要写编辑器扩展?

我总结了几类最常见的场景,你看看有没有戳中你的痛点:

  • 批量操作:比如批量修改材质参数、批量重命名资源
  • 自定义Inspector:让策划同学在Inspector里看到更友好的界面
  • 工具窗口:像项目管理器、数据配置面板这类独立窗口
  • 自动化流程:资源导入后自动处理、打包前自动检查
  • 可视化调试:在Scene视图里画线、画框,辅助调试

你想想看,如果每次改个参数都要手动打开十个窗口,那效率得多低?编辑器扩展就是来解决这个问题的。

第一个Editor脚本:从零开始

好,咱们直接上手写代码。我习惯先创建一个Editor文件夹,这是Unity的约定——所有编辑器脚本都要放在这个文件夹里,或者它的子文件夹下。

小提示:Editor文件夹可以放在Assets根目录,也可以放在任意子目录下。但建议统一放在Assets/Editor/里,方便管理。

来看一个最简单的例子:

// 文件路径:Assets/Editor/MyFirstEditorTool.cs
using UnityEngine;
using UnityEditor;

public class MyFirstEditorTool
{
    [MenuItem("Tools/我的第一个工具")]
    public static void ShowTool()
    {
        Debug.Log("Hello from Editor Extension!");
    }
}

写完这段代码,回到Unity编辑器,你会看到顶部菜单栏多了一个「Tools」菜单,下面有个「我的第一个工具」。点击它,控制台就会打印出那句话。

是不是很简单?但这里有个坑——我曾经忘记把脚本放在Editor文件夹里,结果打包时报错,找了半天才发现是编辑器代码混进了游戏代码里。

注意:编辑器脚本如果放在非Editor文件夹,会被编译进游戏包体,导致打包失败或运行时异常。一定要放在Editor文件夹下!

MenuItem属性的更多玩法

刚才我们用[MenuItem]属性创建了一个菜单项。这个属性其实挺灵活的,我常用的几种写法:

写法 效果
[MenuItem("Tools/工具A")] 在Tools菜单下创建子菜单
[MenuItem("GameObject/我的工具", false, 10)] 在GameObject右键菜单里添加,优先级10
[MenuItem("Assets/我的工具")] 在Project面板右键菜单里添加
[MenuItem("CONTEXT/组件名/功能")] 在组件的右键菜单里添加

我个人最常用的是CONTEXT那个。比如给Transform组件加个「重置位置到原点」的功能,策划同学用起来特别顺手。

验证菜单项是否可用

有时候我们希望菜单项只在特定条件下才可点击。比如选中了某个类型的对象才显示。这时候可以用第二个参数:

[MenuItem("Tools/处理选中物体", true)]
public static bool ValidateSelection()
{
    return Selection.activeGameObject != null;
}

[MenuItem("Tools/处理选中物体")]
public static void ProcessSelection()
{
    // 处理逻辑
    Debug.Log("处理了: " + Selection.activeGameObject.name);
}

注意看,两个方法用了同一个菜单路径。第一个方法返回bool,用来控制菜单是否可用。第二个方法才是实际执行的逻辑。

为什么会这样设计?我猜Unity是想让验证和执行分离,这样代码更清晰。你想想看,如果验证逻辑写在执行方法里,那菜单的灰显状态就没法实时更新了。

Editor脚本的编译流程

这里有个知识点,我觉得有必要提一下。Unity的脚本编译是分阶段的:

  • 第一阶段:编译Runtime脚本(非Editor文件夹下的脚本)
  • 第二阶段:编译Editor脚本(Editor文件夹下的脚本)

这意味着:Editor脚本可以引用Runtime脚本,但Runtime脚本不能引用Editor脚本。如果你在Runtime脚本里用了UnityEditor命名空间,编译会直接报错。

避坑指南:我曾经在MonoBehaviour里写了using UnityEditor;,结果打包时死活编译不过。后来用#if UNITY_EDITOR预处理指令才解决。

正确的做法是:

using UnityEngine;

public class MyBehaviour : MonoBehaviour
{
    void Start()
    {
#if UNITY_EDITOR
        Debug.Log("只在编辑器中执行");
#endif
    }
}

小结一下

这一讲我们聊了编辑器扩展是什么、能干什么、怎么开始写第一个脚本。说白了,编辑器扩展就是帮你偷懒的工具——把重复劳动交给代码,把时间留给真正有价值的事情。

下一讲我们会深入EditorWindow,做一个真正能用的工具窗口。到时候你会看到,编辑器扩展能做的事情远比今天展示的要多得多。

嗯,今天就到这里。记得把第一个脚本跑起来,感受一下「自己改编辑器」的快感。