You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity 包被移除后重新编译前触发回调的实现方案咨询

问题

开发可分发的Unity自定义翻译辅助包,当前仅支持Unity原生Text组件,计划新增TMPro组件兼容。初始实现采用条件编译标签屏蔽无TMPro环境下的相关代码,已完成三类基础逻辑:条件编译基础逻辑、TMPro导入时自动添加对应标签、导入自定义包时检测到TMPro已存在则自动添加标签。现存问题:当TMPro被从项目中移除时,无法在Unity触发重编译前移除对应的条件编译标签,会因找不到TMPro相关类型导致编译崩溃。要求方案可同时兼容Unity 2019、2020、2021三个版本,可接受无需依赖程序集引用的替代实现方案。

解决方案

方案1:基于资源删除回调的宏自动清理(适配现有条件编译逻辑)

Unity 2019-2021全版本提供的AssetModificationProcessor接口会在资源删除操作执行前触发回调,时机早于Unity重编译流程,完全可以用来提前清理条件编译宏,避免编译报错。
实现步骤如下:

  • 在自定义包的Editor程序集目录下新建脚本,注意该脚本绝对不能引用任何TMPro命名空间下的类型,否则TMPro删除后脚本自身会先触发编译错误。
  • 继承AssetModificationProcessor实现OnWillDeleteAsset静态回调,检测到TMPro相关资源被删除时,自动移除你定义的TMPro相关条件编译宏。
  • 补充编辑器启动兜底检测,覆盖用户手动修改manifest、删除Library目录等不触发Unity资源删除回调的极端场景。

核心实现代码:

#if UNITY_EDITOR
using UnityEditor;
using System;
using System.Linq;
using System.Collections.Generic;

public class TMPDefineGuard : AssetModificationProcessor
{
    private const string TMP_MACRO = "USE_TMPRO"; // 替换为你实际使用的条件编译宏名

    // 资源删除前触发,早于重编译流程
    private static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options)
    {
        // 匹配TMPro包目录或核心程序集,覆盖包管理器安装、本地嵌入等所有TMPro部署形式
        if (assetPath.Contains("com.unity.textmeshpro") || assetPath.EndsWith("Unity.TextMeshPro.dll"))
        {
            SyncTMPDefine(false);
        }
        return AssetDeleteResult.DidNotDelete;
    }

    // 编辑器启动时兜底检测
    [InitializeOnLoadMethod]
    private static void InitCheck()
    {
        bool tmpExists = AppDomain.CurrentDomain.GetAssemblies()
            .Any(asm => asm.GetName().Name == "Unity.TextMeshPro");
        SyncTMPDefine(tmpExists);
    }

    private static void SyncTMPDefine(bool enableTMP)
    {
        var currentGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
        string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentGroup);
        var defineList = new List<string>(defines.Split(';', StringSplitOptions.RemoveEmptyEntries));
        
        bool hasMacro = defineList.Contains(TMP_MACRO);
        if (enableTMP && !hasMacro)
        {
            defineList.Add(TMP_MACRO);
            PlayerSettings.SetScriptingDefineSymbolsForGroup(currentGroup, string.Join(";", defineList));
        }
        else if (!enableTMP && hasMacro)
        {
            defineList.Remove(TMP_MACRO);
            PlayerSettings.SetScriptingDefineSymbolsForGroup(currentGroup, string.Join(";", defineList));
        }
    }
}
#endif

该方案没有版本兼容问题,完全复用你现有的条件编译逻辑,不需要重构核心功能代码。

方案2:反射动态适配方案(零条件编译,无编译报错风险)

如果不想维护条件编译宏的增删逻辑,可以完全放弃条件编译,全程通过反射动态检测TMPro存在性、绑定TMPro组件,代码中不直接引用任何TMPro类型,无论项目是否安装TMPro都不会触发编译错误。
实现逻辑:

  • 抽象文本组件通用适配器接口,定义翻译功能需要用到的文本操作属性、方法(如Text属性、字体设置、颜色设置等)。
  • 原生Text组件适配器直接引用UnityEngine.UI.Text实现,该类型属于UGUI内置模块,不存在缺失问题。
  • 运行时/编辑器初始化时,通过反射遍历已加载程序集查找TMPro.TMP_Text类型,如果找到则动态生成TMPro适配器,通过反射或表达式树缓存属性访问委托,避免频繁反射的性能开销;如果找不到则直接跳过TMPro适配逻辑。
  • 组件扫描时,对挂载在物体上的组件,分别匹配原生Text类型、反射得到的TMP_Text类型,匹配成功则绑定对应适配器。

核心类型检测代码参考:

private static Type _tmpTextType;
public static Type TMPTextType
{
    get
    {
        if (_tmpTextType == null)
        {
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                _tmpTextType = asm.GetType("TMPro.TMP_Text");
                if (_tmpTextType != null) break;
            }
        }
        return _tmpTextType;
    }
}

该方案的反射开销仅在初始化阶段产生一次,运行时文本更新通过缓存的委托执行,性能和直接引用几乎无差异,完全满足翻译插件的使用需求,也不需要处理任何宏同步逻辑,稳定性更高。


内容的提问来源于stack exchange,提问作者Alexis FERRER

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 13:15:26