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
相关产品推荐
相关产品推荐

