大约两年前我第一次给项目接 HybridCLR 时,心里其实没底。当时团队的需求很直接:换包审核周期太长,运营活动想按天更新,美术资源已经能热更了,但是 C# 业务逻辑一直卡在“只能整包”这一步。市面上能选的方案无非 Lua 系、ILRuntime、HybridCLR,后来被 HybridCLR 吸引的原因是它能做到“写完 C# 直接热更,不需要把业务代码翻译成另一门语言”。这篇文章不打算复述官方 README,而是把从零接入到上线这套链路中真正影响落地的细节讲清楚,包括程序集怎么规划、启动器怎么写、AOT 元数据为什么要手动补、版本更新怎么闭环、以及我们犯过的几个典型错误。
如果你正准备给 Unity 项目接 HybridCLR,或者已经在接入但正在被 MissingMethodException、AOT 泛型这类问题折磨,那这篇文章应该能帮你少走不少弯路。我尽量按自己实际操作时的顺序来讲。
1. 为什么选 HybridCLR:方案对比和核心原理
1.1 热更到底在更什么
很多刚接触“热更新”的开发者会把三件事混在一起:改配置、换资源、更新代码。改配置很简单,JSON 或者 ScriptableObject 从服务器拉一份就行;换资源是 AssetBundle 或 Addressables 的专职工作;而最麻烦的是改代码逻辑。
代码热更的本质,是让已发布的客户端不需要下载新安装包,也能执行一段“发布时还不存在”的 C# 逻辑。Unity 默认的打包流程做不到这一点:C# 代码经过编译后,最终会变成 IL2CPP 生成的 native 机器码打进包里,发布后你没法单独替换某一段函数。
所以很多团队早期谈“热更”时,语境其实就是“Hotfix”——把可能出现 bug 的逻辑提前放到一个可替换的位置。在 Unity 里做代码热更,核心不是资源流程,而是你对“程序集如何编译、如何加载、如何执行”这件事的理解。
1.2 三条主流的 C# 热更路线怎么选
真正到了方案选型这一步,团队里会吵起来。我见过不少项目从一开始就分成三派:Lua 派、ILRuntime 派、HybridCLR 派。粗略对比是这样:
| 方案 | 学习成本 | 类型体验 | 性能表现 | 接入坑点 |
|---|---|---|---|---|
| Lua + xLua/sLua | 高,业务要写 Lua | 弱类型,和 C# 交互需要桥 | 跨语言调用有开销,逻辑量大时不容易控制 | 编辑器下的类型检查弱,大量 object 转换 |
| ILRuntime | 中,仍然写 C# | 接近 C#,但泛型和反射有限制 | 解释执行开销比 Lua 方案小 | 偏向自研虚拟机方案,需要理解它的运行模型 |
| HybridCLR | 较低,业务保持 C# | 基本就是 C# | 补在 IL2CPP 之后,运行模型更接近 native | 有 AOT 边界要理解,不能盲目把主包全部热更 |
我这么说不是要踩其他方案。如果你的团队已经写惯了 Lua,或者项目里有一整套 Lua 框架,那没必要为了换而换。但如果你是一个以 C# 为主力语言、希望“一份代码到处跑”的项目,HybridCLR 的吸引力会非常大。
它解决的核心痛点是类型割裂。Lua 和 C# 之间传一个复杂对象通常要么转表,要么走 userdata,调试时经常要猜类型;ILRuntime 虽然也是 C# 语法,但它对跨域继承、泛型特化这些场景仍然有限制。HybridCLR 最大的不同是它并不把热更代码放到另一个隔离运行时里,而是把它塞进 Unity 自己的 IL2CPP 执行链路中,所以业务代码写起来几乎没有“我是在写热更代码”的别扭感。
1.3 HybridCLR 的原理其实不玄乎
HybridCLR 的完整技术细节我不展开,但有一个核心认知必须建立:它是在 IL2CPP 的基础上加了一个解释器模块。
Unity 使用 IL2CPP 时,会把 C# 编译成 IL,然后再转成 C++,最后编译成各平台的 native 机器码。这一套流程的好处是性能接近原生、跨平台一致性好,代价是“代码已经变成机器码了”,你想替换其中某个函数根本没地方下手。
HybridCLR 的做法是在 IL2CPP 的运行时里保留一个能解释执行 IL 指令的模块。当客户端在运行时下载了一个新的热更程序集 DLL,IL2CPP 遇到“这个类型或方法不在 AOT 编译结果中”时,就会把指令交给解释器去执行。于是,你写的还是普通 C#,编译产物还是 DLL,但在发布后的客户端里,这个 DLL 可以被加载和执行。
不过这带来两个重要推论。
第一,主包里仍然会有一批固定编译进 native 的程序集,通常称它们为 AOT 程序集。业务要想热更,就得放一部分代码到独立的热更程序集中。第二,AOT 程序集经过 IL2CPP 和裁剪之后,有些元数据可能不在包里,热更层一旦引用了这部分信息,就可能报错。这也是后面所有坑的根源。
2. 接入前准备:环境、工具链与程序集规划
2.1 版本和构建环境要求
HybridCLR 不是一个“装上就能跑”的独立插件,它需要和当前 Unity 版本、IL2CPP 版本严格对应。所以第一件事不是写代码,而是检查环境。
我的建议是优先使用官方支持列表里的 LTS 版本,比如 2021.3 LTS 或 2022.3 LTS。你正在用的 2020.3 只要在支持范围内也可以,但版本越老,能匹配的 HybridCLR 版本就越少。打包目标建议直接选 IL2CPP 后端,因为 HybridCLR 的主场景就是 IL2CPP。
如果你要打 Android,记得在 Unity Hub 里装好 Android Build Support、Android SDK、NDK 和 OpenJDK。很多人卡在安装器那一步,其实不是 HybridCLR 的问题,而是本机 NDK 版本和 Unity 要求的不一致。我的习惯是 Unity 推荐哪个 NDK 版本就装哪个,不要自己随便换新。
如果你要打 iOS,那需要一台 Mac 和 Xcode。从我们实际项目的结论来看,Hy