这份指南面向已有 国际版 Unity 手游、希望低成本迁移到微信小游戏的项目。主线参考微信小游戏官方的「Unity 适配解决方案」与「快适配」文档,并结合一次真实项目(Unity 2021.3、中重度模拟经营类)从评估到上线的实践。全文按国际版 Unity 工具链书写,不涉及国内特供引擎与相关云服务。核心结论是:能转,但不是「一键转换」——核心玩法代码基本不用重写,但资源加载、启动性能、内存峰值这三件事必须重做。
目录
- 一、先看结论
- 二、平台差异与性能预算
- 三、转换路线:五个阶段
- 四、资源加载方案选型
- 五、环境与工具链准备
- 六、资源加载改造(AB / AA 实操)
- 七、首包与启动优化
- 八、内存优化
- 九、CPU / GPU 与运行性能
- 十、代码层改造
- 十一、微信 SDK 接入与原生 SDK 替换
- 十二、音频适配
- 十三、插件兼容性与代码剥离
- 十四、包体瘦身
- 十五、调试、性能测试与云测试
- 十六、发布上线与现网监控
- 十七、踩坑清单
- 十八、工时估算与排期
- 十九、参考资料
一、先看结论
- 技术上可行:微信适配方案基于 WebAssembly,支持国际版 Unity 2018~2022,大部分第三方插件可以复用,核心逻辑不用重写。
- 成本主要在工程改造,不在玩法代码:资源加载方式、启动流程、内存模型、平台 SDK 四块需要系统改造。
- 优先级不能颠倒:先跑通(阶段二)→ 再压首包和启动(阶段四)→ 再压内存峰值 → 最后才是画面与体验打磨。一上来就优化画质,很容易在内存和启动上翻车。
- 内存是生死线:iOS 低档机超过 1G、中高档机超过 1.4G 就有大概率被系统 OOM 杀掉重启,这不是「卡不卡」的问题,是「活不活」的问题。
- 必须改掉 Resources 主路径:国际版 Unity 没有「资源自动流式托管」捷径,首包与内存要压下来,只能走 Addressable / AssetBundle + 自建 CDN。
- 收益是真实的:即点即玩、免安装、微信买量与社交裂变,这些都是原生 App 拿不到的。
二、平台差异与性能预算
先理解差距,再定预算,最后才是动手优化。
2.1 内存差异
- WASM 编译内存:导出目录
webgl/Build/xxx.code.unityweb(或code.wasm)本身就有几十 MB,浏览器内核编译执行时还会产生更多内存,加上缓存与 JIT 优化,总体大致是 Wasm 文件大小的 10 倍左右。这块开销原生 App 完全没有。 - Emscripten 文件系统:WebGL 出于沙盒机制无法访问本地文件系统,Emscripten 用 JS + IndexedDB 模拟了一套文件系统。Wasm 访问 JS 层,JS 层再定期与 IndexedDB 同步,所以 JS 中始终存有所有文件的一份 copy。这也是为什么官方反复强调「首包资源与 AssetBundle 都不要用 Unity 自带的文件 Cache」。
- 内存是「只增不减 + 有碎片」的:UnityHeap 扩容后不会缩回,单帧内瞬间分配过多对象极容易产生尖刺并触发崩溃。
小程序进程内存可以用一个粗略的公式拆开:
进程内存 ≈ 小游戏基础库 + Canvas + WASM 编译内存
+ UnityHeap + Gfx 显存 + 音频 + JavaScript 堆
以 iOS 高性能模式下、代码包 30MB 的游戏为例:
| 组成 | 典型占用 | 说明 |
|---|---|---|
| 小游戏基础库 | 100~150MB | 公共库 + 运行时容器 |
| Canvas | 70~80MB | 与设备物理分辨率相关,如 iPhone 11 Pro Max 约 80MB |
| WASM 编译内存 | ~300MB | 30MB 未压缩代码在 iOS 上约需 300MB 运行时编译内存 |
| UnityHeap | 256 / 496 / 768MB | 托管堆 + 本机堆 + 原生插件,按游戏重度预留 |
| Gfx 显存 | 视纹理而定 | 纹理与模型 Upload GPU 之后 |
| 音频 | 取决于并发数 | 同时存在的音频建议不超过 20 个 |
| JavaScript 堆 | 通常 < 100MB | 若明显大于 UnityHeap,优先怀疑用了 Unity Cache 做文件缓存 |
结论:基础内存(基础库 + Canvas + 编译内存)大约 500MB 是原生 App 没有的。如果游戏要支持低档机型、把内存压到 1G 以内,业务侧(UnityHeap + Gfx + 音频 + JS)就必须控制在 500MB 左右。
官方建议的内存峰值红线:
| 平台 | 低档机 | 中高档机 |
|---|---|---|
| Android | < 1.2G | < 1.5G |
| iOS | < 1G | < 1.4G |
iOS 低档机主要是 iPhone 6sp / 7 / 8(2G RAM),中高档机是 iPhone 7P / 8P / X / 11 及以上(3G RAM 起)。真机安全峰值建议控制在 1.2~1.3G。
2.2 CPU 差异
- Wasm 大约比原生 App 慢 3 倍。
- Wasm 是单线程的,且不支持 SIMD,所以 WebGL 小游戏的 CPU 性能天然低于原生。
- 多线程 API 不可用,所有并发逻辑要用异步、分帧、协程来替代。
- 结论:CPU 密集玩法(大量实时寻路、复杂物理、逐帧大量计算)要重新评估,必要时降规格或改成异步分帧。
2.3 GPU 差异
- WebGL 只支持 WebGL 1.0 / 2.0,功能大致对应 OpenGL ES 2.0 / 3.0。
- 转换工具默认走 WebGL 1.0,WebGL 2.0 属于 beta/受限能力,依赖 iOS 高性能+ 与较高系统版本,上正式版要谨慎。
- 移动端压缩纹理:Android 用 ETC2 / ASTC,iOS 用 ASTC,PC 端微信与开发者工具用 DXT / BC7(不支持时软解为 RGBA32)。
2.4 预算表(建议写进提测 Checklist)
| 指标 | 目标 | 红线 |
|---|---|---|
| 首包网络传输量 | 3MB 左右 | 不超过 5MB |
| 小游戏总包体 | — | ≤ 20MB |
| WASM 代码包 | ≤ 30MB | 分包后约为原始 1/3 |
| 首屏启动 | 5~10s | 越短越好 |
| 内存峰值 | Android < 1.2G / iOS < 1G | Android < 1.5G / iOS < 1.4G |
| 单资源文件 | 2~5MB | 单请求 ≤ 100MB(超时 60s) |
| 同时播放音频 | ≤ 20 个 | — |
三、转换路线:五个阶段
官方把接入拆成五个阶段,建议直接按这个节奏推进,每个阶段都有明确的准入条件,避免「边做边发现方案不行」。
| 阶段 | 名称 | 核心工作 | 交付物 / 放行条件 |
|---|---|---|---|
| 一 | 兼容性评估 | 引擎版本、渲染管线、网络协议、第三方插件、原生 SDK 盘点 | 可行性结论 + 方案选型 |
| 二 | 项目转换 | 安装插件、WebGL 导出、转换小游戏、资源上传 CDN | 真机能跑通主流程 |
| 三 | 平台能力接入 | WX SDK(登录/广告/分享/开放数据域/存储)、屏幕与输入法适配、安全域名 | 平台功能可用,原生 SDK 已下线 |
| 四 | 体验调优 | 首包与首帧、WASM 分包、压缩纹理、AB 卸载、内存峰值、音频 | 各项指标进入预算表 |
| 五 | 发布上线 | 审核发布、云测试验收、现网监控与版本更新 | 上线并有人盯数据 |
四、资源加载方案选型
这是整个迁移里最贵、也最关键的决策。国际版 Unity 没有国内特供的「Resources 自动远端流式」能力,主线只能是 Addressable / AssetBundle + 自建对象存储与 CDN。先定方案再动工。
4.1 Addressable(推荐)
用 Addressable Assets System(AA)做按需加载、依赖解析与更新。
- 官方指引:https://developers.weixin.qq.com/minigame/dev/guide/game-engine/unity-webgl-transform/Design/UsingAddressable.html
- 优势:
- 依赖与分组清晰,远程 Catalog / 更新链路成熟;
- 原生 App 与小游戏可共用同一套资源体系;
- 卸载与引用计数更好做,利于压内存峰值。
- 劣势:
- 初次接入要理清 Group、Label、Catalog 与远程路径;
- 需要自己维护 CDN、缓存策略和版本更新。
4.2 AssetBundle
直接用 AssetBundle(AB)做分包下载与加载,自行封装更新与依赖。
- 官方指引:https://developers.weixin.qq.com/minigame/dev/guide/game-engine/unity-webgl-transform/Design/UsingAssetBundle.html
- 优势:
- 概念更薄,老项目若已有 AB 管线改造成本更低;
- 对包体拆分与首包裁剪控制很直接。
- 劣势:
- 依赖、版本、缓存、回滚都要自己管;
- 中重度项目后期维护成本通常高于 AA。
4.3 决策建议
| 项目情况 | 推荐 | 理由 |
|---|---|---|
| 新项目,或愿意一次性把资源体系做干净 | Addressable | 长期维护成本更低 |
| 已有稳定 AB 管线,短期只要小游戏能跑 | AssetBundle | 改造面更可控 |
| 需要同时支持原生 App 与小游戏 | Addressable 或统一 AB | 一套资源体系全平台复用 |
| 中重度、内存压力大 | Addressable(细分组 + 及时卸载) | 分包与卸载粒度更可控 |
| 仍大量使用 Resources / 一次性全量加载 | 先迁 AA/AB,再谈优化 | Resources 会无条件进首包 |
经验:只要项目里 Resources 和「一次性加载全量资源」的逻辑比较多,AA/AB 改造就要按 2 周以上估。这部分是整条链路的水下冰山。
4.4 云存储与 CDN
- 对象存储:腾讯云 COS、又拍云 USS 等,用于托管远端资源。
- CDN:用于资源下载加速,例如腾讯云 CDN:https://buy.cloud.tencent.com/cdn_package。
- CDN 必须开启 Brotli 或 gzip 压缩,否则首包资源体积会非常难看。
- 小游戏框架默认以 HTTP/2 多路复用流式下载资源;CDN 未开启 HTTP/2 会自动降级为 HTTP/1.0,下载速度会明显变慢。
- 安全域名(
request/download/socket)必须在 MP 后台配置,否则正式版请求会被拦截。
五、环境与工具链准备
5.1 引擎版本(国际版 Unity)
下表只列国际版 Unity。统计口径会随官方更新,动手前以推荐引擎版本为准。
| 引擎版本 | 压缩纹理 | 相对编译体积 | 备注 |
|---|---|---|---|
| 2018 | 仅 DXT,不支持 ETC2/ASTC | 100% | 不支持设置 dpr 分辨率 |
| 2019 | 仅 DXT,不支持 ETC2/ASTC | 100% | — |
| 2020 | DXT/ETC2,不支持 ASTC | 100% | — |
| 2021 | 全支持 DXT/ETC2/ASTC | 80% | 需自己在 WebGL 设置里配 ASTC |
| 2022(推荐) | 全支持 DXT/ETC2/ASTC | 80% | 强烈建议 2022.3 LTS |
实践选择:
- 新项目:直接用 Unity 2022.3 LTS,压缩纹理、编译体积、迭代速度都更好;2022 还可配合 Memory Profiler 分析 WebGL 内存。
- 老项目:2018~2020 暂时不升也能上,但必须配合「微信压缩纹理工具」优化内存;2018 还改不了 dpr。有人力强烈建议升到 2022,编译也更快,利于迭代。
- 2021.3 是可用的折中版本:支持 ASTC(移动端最广覆盖),编译体积约为老版本的 80%。注意这是「支持」不等于「默认开启」,要手动在纹理的 WebGL Settings 里配置格式。
- 从 Unity Hub 安装国际版时,必须勾选 WebGL Build Support。
5.2 转换插件
国际版 Unity 安装微信官方 WX SDK / 转换工具即可,按官方安装指引操作。
- 安装指引(Package Manager / UnityPackage):https://developers.weixin.qq.com/minigame/dev/guide/game-engine/unity-webgl-transform/Design/SDKInstaller.html
- Unity 2019+ 推荐:
Window → Package Manager → + → Add package from git URL,URL 以安装指引页当前公布为准 - Unity 2018 或 Package Manager 不便时:用 UnityPackage 离线包
- 文档与设计稿仓库:https://github.com/wechat-miniprogram/minigame-unity-webgl-transform
- 官方文档入口:https://developers.weixin.qq.com/minigame/dev/guide/game-engine/unity-webgl-transform.html
5.3 微信开发者工具
- 使用 Stable 稳定版,不要用小游戏版 Minigame Build,避免稳定性问题。
- 需要在 MP 平台开通的能力:
- 能力地图 → 生产提效包 → 快适配;
- 能力地图 → 研发能力 → 生产提效包 → 高性能模式(iOS 性能不足时必开);
- 开发设置 → 服务器域名(request / download / socket)。
5.4 目录结构与调试
转换后会得到两类产物:
minigame/ ← 小游戏包,用微信开发者工具打开
webgl/ ← 资源目录,上传到 CDN
├── Build/ code / framework / data / symbols
├── StreamingAssets/ AssetBundle / Addressables
├── Assets/
└── texture-config.js
- PC 端开发者工具可以用登录态/测试 Token 直接跳过登录,方便本地调试;这类凭据不要写进仓库,放在项目内部文档或本地配置里。
- 调试技巧:先用 JS 直接改
minigame里的 JS 验证问题,确认后再改 Unity 工程用 C# SDK 落地,迭代最快。
六、资源加载改造(AB / AA 实操)
6.1 首包只放 Loading
首包资源(webgl/Build 下 data 文件)主要由以下内容组成:
unity default resources:引擎默认资源(Arial 字体、默认 mesh、纹理等);il2cppmetadata:IL2CPP 生成的类与方法信息;unity builtin_extra:Always Included 的 Shader;- BuildSettings 里所有 active 的场景;
- Resources 目录里的资源及其引用链;
- 全局设置及引用到的资源(如 splash 图)。
首包原则:
- 只保留 Splash / Loading 场景,不要勾选任何其他场景;
- 不要把字体打进首包(字体压缩率极低),中文用 2~3MB 以内的精简字体;
- 不要往 Resources 放资源,该目录会被无条件打进首包;
- 首包压缩后体积以 3MB 左右最佳,不超过 5MB。
6.2 Addressable 改造
Addressable 是官方推荐的迁移路径,最小改造量的做法:
Resources.Load→Addressables.LoadAssetAsync<T>;- 直接
Instantiate(prefab)→AssetReference.InstantiateAsync(),否则预制体及其全部依赖必须在场景加载前就绪,会严重拖慢初始化; - 每个场景单独作为一个 Addressable Group,用
Addressables.LoadSceneAsync动态加载; - 用 Tools → Analyze 检查冗余,必要时「Check Duplicate Bundle Dependencies → Fix Selected Rules」自动消除重复依赖;
- iOS 内存压力大时,把 Provider 换成 WXAssetBundleProvider(
WXAssetBundleProvider.cs放进WX-WASM-SDK-V2/Runtime/,并让该目录引用Unity.ResourceManager),可以显著减轻 iOS 内存压力。
异步写法三种任选:
// 回调
Addressables.LoadAssetAsync<Texture2D>("mytexture").Completed += handle => { /* ... */ };
// 协程
var handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
if (!handle.IsDone) yield return handle;
if (handle.Status == AsyncOperationStatus.Succeeded) { var tex = handle.Result; }
// await
var handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
await handle.Task;
6.3 AssetBundle 打包参数
BuildAssetBundleOptions.AppendHashToAssetBundleName // 文件名带 hash
| BuildAssetBundleOptions.ChunkBasedCompression // LZ4,加载速度与体积均衡
| BuildAssetBundleOptions.DisableWriteTypeTree // 不需要兼容新旧 Unity 时,减小体积、加快加载
AppendHashToAssetBundleName:小游戏底层缓存与淘汰以 hash 为依据,这是资源更新能生效的关键。ChunkBasedCompression(LZ4):相比 LZMA 加载更快,体积略大,综合最优。DisableWriteTypeTree:不需要新老引擎兼容时开启,体积更小、加载更快、内存更低。- 严禁使用
WWW.LoadFromCacheOrDownload、LoadFromFile等缓存 API,直接用UnityWebRequestAssetBundle按需异步加载,缓存的判断与淘汰由适配层负责。
6.4 Shader 丢失
AB 资源加载后 Shader 丢失是高频问题,两种解法:
- 把用到但没被引用的 Shader 放入 Always Included Shaders;
- 收集并设置 ShaderVariantCollection,配合 Shader 异步 Warmup。
另外注意:场景或 AB 中静态摆放的物件如果没单独设为 Addressable,会随场景打进同一个 bundle,容易造成重复冗余,务必用 Analyze 检查。
6.5 AB 缓存机制与更新
小游戏底层对 bundle 有缓存:只要 bundle 包名不变,缓存过就不会再拉取,即使更新了资源或换了 CDN 路径也一样。所以必须靠文件名里的 hash 让 URL 变化,从而触发重新下载。
关键配置(Assets/WX-WASM-SDK-V2/Editor/MiniGameConfig.asset):
bundlePathIdentifier: StreamingAssets;bundles // 命中该路径标识符的才自动缓存
bundleHashLength: 32 // 文件名中 hash 的长度,默认 32
bundleExcludeExtensions: .json;.hash // 这些后缀不缓存
texturesHashLength: 8 // 压缩纹理工具产物的 hash 长度
maxStorage: 200 // 最大缓存容量(MB),接近阈值自动回收
注意事项:
- 配置文件不要带缓存:AA 的
catalog.json/setting.json、AB 的 AssetBundleManifest 都不带 hash,一旦被 CDN 缓存就会永远加载旧资源。两种做法:每次发版更换 CDN 路径(Version_1/Version_2),或对这些文件在源站/CDN 设置no-cache。 - 缓存命中判定依赖「资源下载 URL 包含 DATA_CDN」且路径包含
bundlePathIdentifier,配置不对就完全不缓存。 - 不同电脑打出来的 AB hash 可能不一致,跟各自
Library目录下的缓存有关,联调时以同一条流水线产出的包为准。 - 除非清楚小程序的更新机制,不要删除旧版本资源,否则老版本用户可能直接报错。
6.6 AB 卸载策略
- 现在的 AB 分包逻辑通常不好区分「哪些该留、哪些该卸」,要提前设计卸载规则。
- 常见触发点:弹窗关闭、场景切换。切换场景时统一卸载不再被引用的 bundle。
- 业务侧一律按「未缓存、需要从网络下载」去写异步逻辑,适配层会判断缓存;不要自己写缓存判断。
6.7 CDN 与服务端注意事项
- 对
.txt后缀(首资源包)开启 Brotli 或 gzip; - 资源下载并发数为 10,超过自动排队;
- 单个请求最大 100MB、超时默认 60s,但建议单文件控制在 2~5MB;
- 网络与安全域名、跨域、SSL 参考官方「网络通信适配」文档。
七、首包与启动优化
7.1 先量化,再优化
把 unity-namespace.js 里的 hideTimeLogModal 设为 false,就能看到启动 timelog。启动主要由三部分决定:
- 首包资源下载:资源越大越慢,绝大部分玩家下载速度约 2MB/s,还有不少 <300KB/s 的低速用户;
- WASM 代码下载与编译:代码包影响着下载时长与初始化编译时间,转换工具会把它 br 压缩到原
code包的约 20%; - 引擎初始化与首帧逻辑:典型 3~6s,且这段是 CPU 密集、网络空闲,正是预下载的黄金窗口。
7.2 首包瘦身
- 用 AssetStudio 检查 data 首包与 AB 的资源内容,找出错误打包和冗余:https://github.com/Perfare/AssetStudio
- 用 BuildReportTool 看每次构建各资源的占用,用 Asset Hunter 清理无用资源。
- 首资源包也可以勾选「压缩首包资源」(Brotli),代价是首次启动可能多约 200ms,主要用于「小游戏包内加载」时省包体。
7.3 WASM 代码分包
把原来一个 wasm 拆成「启动主包 + 延迟加载子包」,可以同时降低启动下载时间、编译时间和运行内存。
- 官方文档:https://developers.weixin.qq.com/minigame/dev/guide/game-engine/unity-webgl-transform/Design/WasmSplit.html
- 工具链:
- 微信开发者工具安装
wasmCodeSplit扩展插件(按 wasm 文件名的 md5 区分版本); - CI 集成用
wasmsplit-ci:https://github.com/wechat-miniprogram/wasmsplit-ci
- 微信开发者工具安装
- 基本流程:
- Unity 导出时开启 Profiling Funcs,保留
webgl.wasm.symbols.unityweb; - 开发者工具里点「启用代码分包」,输入版本描述,上传代码包;
- 等待预处理,选择是否增量分包;
- 分别用 Android 与 iOS 真机收集函数;
- 生成 profile 包测试,确认没有
wait for func或fetch js; - 生成 release 包发布。
- Unity 导出时开启 Profiling Funcs,保留
- CI 用法:
wasmsplit-ci init -p <项目路径> -k <私钥.pem> -d <版本描述>
wasmsplit-ci getinfo -p <项目路径> -k <私钥.pem>
wasmsplit-ci dosplit -p <项目路径> -k <私钥.pem> --release
# 增量分包时,init 追加 -r <参考版本的 code MD5>
- 产出目录:
wasmcode(主包)、wasmcode1(Android 子包)、wasmcode2(部分方案下的 iOS 子包)、webgl.wasm.symbols.unityweb(符号文件,保留用于还原函数名)。 - 注意:分包每次发版都要重新走一遍收集与生成,建议放到发版流水线,不要等上线前手忙脚乱。
7.4 预下载
在引擎初始化这段「网络空闲期」提前把后续资源下载好并缓存到本地,下次用到时直接读缓存。
- 配置:导出面板的预下载列表,或运行时通过 C#
WX.SetPreloadList(list)、JSGameGlobal.manager.setPreloadList(list)动态设置; - 并发:引擎初始化前默认 10 个并发,初始化后降为 1 个,可用
WX.PreloadConcurrent/GameGlobal.manager.setConcurrent调整; - 原则:
- 预下载总体积控制在 3~5MB;
- 文件数量 ≤ 10 个;
- 必须是插件会自动缓存的文件,否则下载无效;
- 按优先级排序,越重要的越靠前;
- 预下载 URL 必须和后续加载 URL 完全一致,否则命中不了缓存。
7.5 首帧逻辑
MonoBehaviour的首帧Awake/Start逻辑要尽可能少,优先把画面呈现出来;- 初始场景不宜过大,能显示 Splash 即可;
- 后续配置与主场景加载要分帧,切勿在
Start/Awake里同步阻塞; - 首帧耗时可以用 Android CPU Profiler 逐帧定位。
7.6 启动封面
Unity WebGL 启动需要时间,微信支持配置封面图/视频作为过渡,可自定义封面内容、加载文案样式、进度条样式、自动隐藏时机。这是留存的第一道防线,不要留默认白屏。
八、内存优化
内存优化的思路是:先测准峰值,再逐个消掉大头。官方给出了最容易出问题的几个位置,基本按这个顺序排查。
8.1 设置合理的 UnityHeap 预留
- 该值只表示对 UnityHeap 峰值的预留,避免运行中扩容产生尖刺;
- 取值方法:
- 导出面板勾选「显示性能面板」,或把
unity-namespace.js的enableProfileStats打开(提审版本必须关掉); - 游戏跑一段时间,观察
DynamicMemory峰值; UnityHeap = DynamicMemory + 少量静态内存(通常 <10MB),预留比峰值多 50~100MB。
- 导出面板勾选「显示性能面板」,或把
- 参考值:超休闲 256 / 中度(模拟经营、卡牌成长)496 / 重度(SLG、MMO)768;
- 不要贪大:
UnityHeap ≥ 1024MB时,大部分设备会直接启动失败;UnityHeap ≥ 500MB时,32 位微信(约 5% 用户)与 iOS 普通模式大概率启动失败。
8.2 降低 WASM 编译内存
- 代码分包工具能把编译内存降低 50% 以上,这是单项收益最大的优化;
- 删除多余插件,减少不必要的引擎模块(物理、数据统计等)。
8.3 减少 GPU 显存
- 用压缩纹理(ASTC)替代 RGBA/DXT,既降显存也降运行时解压开销;
- Unity 2021 及以上直接用引擎 ASTC;2018~2020 必须用微信压缩纹理工具;
- 关闭 HDR:标准管线在 Graphics Settings → tier2 取消 Use HDR,URP 在 renderer 配置里取消;
- GPU 压力大的游戏开 iOS 高性能+模式,可显著降低 GPU 内存。
8.4 首包与 AssetBundle 内存
- 首资源包永远占用内存且无法释放,所以首包越小越好;
- AB 被使用时会在 Unity Native 内存中解压,AB 越大瞬时峰值越高,要拆小并及时释放;
- 绝对避免首包和 AB 使用 Unity 自带的文件 Cache;
- 检查是否有「切场景不卸载」「弹窗关闭不卸载」导致的泄漏。
8.5 音频内存
- 不要用 FMOD 播放长音频(如 BGM),FMOD 全部走 WebAudio,占用内存大;
- 控制音效数量,同时存在的音频不超过 20 个;
- 尽量强制单声道,双声道会产生 2 倍内存消耗。
8.6 运行时内存告警
注册内存告警监听,收到告警后主动清理:
wx.onMemoryWarning(function () {
// 收到内存告警,主动触发 GC 并释放可释放的资源
wx.triggerGC();
});
再配合业务侧的「释放非当前场景资源、清理对象池、丢弃临时纹理」等逻辑。
8.7 内存检测工具
| 层级 | 工具 | 用途 |
|---|---|---|
| 进程级 | PerfDog / Xcode Instrument / Android Studio | 看真机进程总内存(iOS 高性能模式看 WebContent 进程) |
| UnityHeap | 性能面板、ProfilingMemory | 托管堆 / 本机堆 / 分配器细节 |
| 引擎与资源 | Unity Profiler | 定位具体资源与对象 |
| JS 堆 | 微信开发者工具 Chrome DevTools 的 Performance / Memory | 发现异常的 JS 内存(优先怀疑 Unity Cache 缓存文件) |
| 云测 | 小游戏云测试 | 多机型内存峰值分布与曲线 |
| 其他 | SoloPi、UWA 微信小游戏工具 | 真机性能与内存分析 |
常见误区:Unity Profiler 里只看到 200MB+ 并不代表没问题。Profiler 只覆盖「引擎可监控内存」,不包含小游戏公共库、Canvas、WASM 编译与容器内存。必须以真机进程内存为准。
8.8 iOS 内存排查步骤
- 测试内存时不要开 development、profilingmem;
- 必须用代码分包 + 压缩纹理;
- 用 PerfDog 或 Instruments 看 WebContent 进程内存,安全峰值 1.2~1.3G;
- 若离 1.5G 上限还很远就崩溃,优先检查 UnityHeap 预留是否足够;
- 打开性能面板看
DynamicMemory峰值,建议不要超过 500M; - 用 PerfDog 看 Android 的 GL / Gfx 显存,显存压力大就开高性能+;
- 以上都做完还有问题,带着详细数据找平台侧分析。
九、CPU / GPU 与运行性能
9.1 打包设置对性能的影响
- IL2CPP Code Generation:
- Faster Runtime(OptimizeSpeed):性能更高;使用 HybridCLR 时只能用这个;
- Faster (Small) Build(OptimizeSize):包体小约 15%,性能略差;转换面板的
Il2CppOptimizeSize勾选即对应此项,官方默认推荐。游戏中若有大量泛型集合高频访问,建议 OptimizeSpeed。
- Managed Stripping Level:官方建议 High,配合 Strip Engine Code 可大幅减小代码包。本项目实际折中用了 Medium,代码包从 24.4MB 降到 13.9MB,减少约 45%。
- Enable Exceptions:Player Settings → Publishing Settings 里设为
None或Explicitly Thrown Exceptions Only,可减小包体、提升性能。 - Strip Engine Code:开启,配合防剥离配置(见第十三节)。
- Profiling Funcs:只在调试时勾选,正式服必须关闭,否则代码包会带可读函数名,白白增大体积。
- WebGL 2.0:beta 能力,iOS 15.5 以下问题较多,建议先关闭并充分测试。
9.2 iOS 高性能模式与高性能+
高性能模式:
- 条件:iOS ≥ 14.0、基础库 ≥ 2.23.1、微信 ≥ 8.0.18,满足率约 90%,不满足则回退普通模式;
- 开通:MP → 能力地图 → 研发能力 → 生产提效包 → 开通高性能模式;
- 配置:
game.json设置"iOSHighPerformance": true; - 内存限制:低端机(6s/7/8,2G RAM)1G,中高端机(7P/8P/X 及以上,3G+)1.5G,安全值 1.2~1.3G;
- 判断是否生效:删除本地小游戏(开发版/体验版/正式版)重新进入,打开调试看 vConsole 日志中
game start的render字段为h5即高性能模式。
高性能+模式:
- 是高性能模式的升级,渲染挪回微信进程,渲染效果与渲染内存都更好,建议 WebGL2、内存压力大的游戏开启;
- 条件:微信 ≥ 8.0.45、iOS ≥ 14(iOS 14 还需微信 ≥ 8.0.49),必须先开高性能模式;
- 配置:
"iOSHighPerformance": true, "iOSHighPerformance+": true; - 判断:
// 高性能模式(安卓无此属性)
if (GameGlobal.isIOSHighPerformanceMode) { /* ... */ }
// 高性能+模式
if (GameGlobal.isIOSHighPerformanceModePlus) { /* ... */ }
必须注意的坑:高性能模式在启动初始阶段存在一次 CPU 高峰(用于 WebAssembly 编译优化),长期运行 CPU 明显低于普通模式,但前期会非常卡。所以要针对启动阶段单独做优化,不能只看平均值。
9.3 渲染与 CPU 优化清单
- 压缩纹理按设备选择(移动端 ASTC,PC DXT/BC7);
- 关闭 HDR,限制纹理 maxsize(平台下建议不超过 1024),UI 不开 Mipmap(会多 1/3 内存);
- 关闭 Read/Write Enabled(会同时占显存与 CPU 内存);
- NPOT 纹理无法使用压缩纹理,导入时要改成 2 的次方;
- 减少每帧 Update 数量、合并 Canvas、控制同屏对象数、减少频繁 UI 开关;
- 使用 Unity Profiler 与 Android CPU Profiler 逐帧定位,重点看
PlayerLoop、GC、Shader 编译、资源实例化。
十、代码层改造
10.1 同步改异步
所有「加载完资源立刻使用」的逻辑都要改成异步:
- 动态加载资源的实例化必须等异步加载完成;
- 引导、弹窗依赖的配置与预制体要改为按需异步触发(本项目就是把引导触发整体异步化,否则首帧被阻塞);
- 需要防点击的界面要有 loading 遮罩,避免异步期间重复触发。
估时参考:3 天起,逻辑越重越多。
10.2 本地数据存取
- WebGL 下没有可靠的「退出时自动保存」,需要手动调用
PlayerPrefs.Save(); - 保存时机:监听
wx.onAppHide()(小游戏切后台)主动落盘; - 不要用
System.File,微信文件存储有容量上限(默认 200MB,满足条件可申请提升到 1G)。
wx.onAppHide(function () {
// 通知游戏侧保存存档
});
10.3 网络层
WebGL 环境下 JS 无法直接访问 IP 套接字,因此:
System.Net(尤其是System.Net.Sockets)、UnityEngine.Network*全部不可用;- HTTP 请求统一改为 UnityWebRequest;
- 全双工通信改用 WebSocket,推荐开源插件 UnityWebSocket(注意用 v2.8.3 及以上,UPM 模式下低版本会报
missing function: WebSocketSetOnOpen); - 服务端若是纯 TCP,需要加一层 WSS ↔ TCP 代理;
- 正式版必须配置安全域名,真机预览可临时「开启调试」跳过校验,开发者工具在「详情 → 本地设置」里控制。
10.4 多线程与文件系统
- 不支持多线程,删除线程用法,改用异步、分帧、协程;
- 不支持
System.File直接读写,文件存储通过 WX SDK; - Lua 支持标准 Lua 与常见 Binding(xlua/tolua 等),但不支持 LuaJIT,需真机验证性能;PuerTS 需要 iOS 14.5 以上。
10.5 Unity 与 JS 互调
在 Assets/Plugins/WebGL 下新建 xx.jslib:
mergeInto(LibraryManager.library, {
JSTestCall: function (str) {
console.log(str);
}
});
打包后 JS 脚本会被拷贝到小游戏工程里,可继续在 JS 侧调试。
10.6 功能与交互调整
- 关闭按钮位置:现有 UI 的关闭按钮容易与微信小游戏自带的关闭按钮位置冲突,需要重新排布;
- 支付:去掉 Google Play / App Store 支付相关逻辑,按需接入微信小游戏支付;
- 首包加载阶段只显示静态图或进度条,不要做动画;
- 打点、广告、支付等与原生强相关的功能在小游戏版本上按需关闭或替换。
十一、微信 SDK 接入与原生 SDK 替换
官方提供 C# 版 WX SDK,能力覆盖:
| 类别 | 能力 |
|---|---|
| 登录 | 微信登录、用户信息 |
| 设备 | 存储、震动、字体(GetWXFont) |
| 开放数据 | 开放数据域(ShowOpenData / HideOpenData)、排行榜、关系链 |
| 广告 | Banner(CreateFixedBottomMiddleBannerAd)、激励视频等 |
| 音频 | PreDownloadAudios |
| 社交 | 游戏圈/聊天组件(CreateMiniGameChat) |
| 插件 | SetDataCDN、SetPreloadList、PreloadConcurrent |
| 其他 | InitSDK、CanIUse、SetDevicePixelRatio、ReportGameStart、HideLoadingPage、OnLaunchProgress |
接入顺序建议:
- 登录:先打通接口与后端验证,再补全功能;
- 广告:激励视频是主要变现,必须与打点、存档、发奖逻辑联调,重点测「看完/中途退出/重复观看」;
- 打点上报:替换原生的打点 SDK,并补上启动留存等关键埋点(官方 Unity Loader 已提供基础上报,游戏内关键帧需要自己上报);
- 分享与排行榜:涉及开放数据域,注意渲染层级与权限;
- 设备能力:存储、震动、字体。
原生 SDK 替换:广告换成微信小游戏广告能力,打点换成小游戏侧统计或自建上报,支付按需接微信小游戏支付。估时约 3 天。
联调准备:准备好运营方与自己的小游戏 AppID、正式/测试 CDN 地址,登录、支付、广告这类能力通常需要后台配置与白名单。
十二、音频适配
UnityAudio 已经自动适配微信小游戏,优先用 UnityAudio,不要一上来就全量替换:
- 长音频走
InnerAudio,短音频走WebAudio,插件按文件大小自动切换; - FMOD 可用,但全部走 WebAudio,大文件(BGM)会占用很大内存,不推荐;
- 推荐格式:mp3 或 aac,双端兼容最好;
- 同时播放的音频数量过多会引起卡顿,建议限制在 20 个以内。
如果确有需要(比如长音频内存压力大),再勾选「使用微信音频 API」并手动替换:
var audio = WX.CreateInnerAudioContext(new InnerAudioContextParam() { src = "Audio/bgm.mp3", needDownload = true });
audio.OnCanplay(() => { audio.Play(); });
// 批量预下载
string[] list = { "Audio/0.wav", "Audio/1.wav", "Audio/2.wav" };
WX.PreDownloadAudios(list, (res) => { /* res == 0 表示成功 */ });
- 使用 CDN 地址时,
InnerAudioContext实例最多同时存在 10 个;本地文件最多 32 个,因此建议先needDownload下载再用; - 播放完成后需要销毁再重建实例;
- 必须等
onCanplay之后再Play; - 已知问题:iOS 17.5+ 退后台回来音效可能无法继续播放;
operateAudio:fail jsapi has no permission在退后台时偶现,可忽略;部分机型微信 8.0.51 前循环播放有问题。遇到问题先升级插件与微信版本。
踩坑记录:某些机型(例如部分 Google Play 版微信客户端)播放音效会报 SystemError(gameSDKScrptError) Yc is not a constructor...,先排查客户端版本与插件版本,多数通过升级解决。
十三、插件兼容性与代码剥离
13.1 兼容性清单(实测)
| 插件 | 结论 | 备注 |
|---|---|---|
| A* Pathfinding | ✅ 可用 | — |
| DOTween | ✅ 可用 | — |
| Spine | ✅ 可用 | 建议一个 spine 资源打一个 AB 组 |
| Full Serializer | ❓ 待验证 | 反射较多,注意防剥离 |
| Variation | ❌ 不可用 | 需替换 |
| FMOD | ⚠️ 可用但有条件 | 长音频会吃内存,不推荐 |
| Lua / xLua / tolua | ✅ 可用 | 不支持 LuaJIT |
| PuerTS | ✅ 可用 | 需 iOS 14.5+ |
| UnityWebSocket | ✅ 可用 | 需 v2.8.3+,否则 UPM 下缺导出 |
13.2 代码剥离与防剥离
调整剥离等级后,IL2CPP 会把「看起来没人用」的类型裁掉,以下情况必须做防剥离:
- 预制体上挂载的脚本;
- 通过反射调用的类与成员;
Activator.CreateInstance动态创建的类;- 注册表/工厂模式里按名字创建的类。
做法:配置 link.xml 保留程序集或类型,或对相关程序集整体设置 preserve="all"。项目实践:剥离等级调到 Medium + 防剥离处理后,代码包 24.4MB → 13.9MB(约 -45%),同时保证了运行期不报 MissingMethodException。
十四、包体瘦身
14.1 代码侧
- 剥离等级:官方建议 High,实际按项目风险取 Medium / High;
- IL2CPP 选 OptimizeSize(体积优先);
- Enable Exceptions 设为 None / Explicitly;
- Profiling Funcs 只留调试包;
- WASM 代码分包。
14.2 资源侧
AB 压缩实测记录(中重度项目,供参考):
| 阶段 | 体积 | 手段 |
|---|---|---|
| 原始 | 170M | — |
| 第一轮 | 158M | LZ4 + 去除 TypeTree + 设置 Shader 变体 |
| 第二轮 | 56M | 字体精简 + 贴图转 ASTC + 整理 AB 分组(防止 bundle 过多) |
注意:第三方统计的 56M 是压缩前,最终网络传输还要叠加 CDN 的 Brotli/gzip。
14.3 纹理优化
用转换工具自带的「资源优化工具」(菜单栏 → 微信小游戏 → 资源优化工具)批量扫描处理:
- 关闭 Read/Write Enabled;
- 关闭不必要的 Mipmap(UI 尤其不要开);
- 合理设置 maxsize(建议不超过 1024,可用「自动减半」);
- 修改纹理压缩格式;
- NPOT 纹理改成 2 的次方。
14.4 压缩纹理
- Unity 2021+:在纹理的 WebGL Settings 里直接配 ASTC,支持 8x8 / 6x6 / 5x5 / 4x4,默认推荐 8x8;4x4 最清晰但内存更高;不支持 ASTC HDR。
- Unity 2018~2020:必须用「微信压缩纹理工具」,按设备 GPU 支持情况按需下载对应格式。
- 配置好后在「微信小游戏 → 包体瘦身 → 压缩纹理」执行处理;调试模式只生成 ASTC,正式发布用全量模式。
- 重度游戏(MMO/SLG)内存压力大时,可结合 WXAssetBundle 减少 AB 体积带来的文件内存。
14.5 字体
- 去掉用不到的字,做精简字体;
- 中文必须自定义字体,控制在 2~3MB 以内,否则会明显拖慢启动。
十五、调试、性能测试与云测试
15.1 调试手段
- 微信开发者工具:看 Console、Network(确认首包实际传输量)、Performance / Memory;注意它不校验安全域名与 SSL,与真机行为有差异,必须在真机复测。
- 真机调试:右上角菜单「开启调试」→ vConsole。
- 启动 timelog:
unity-namespace.js的hideTimeLogModal = false。 - 性能面板:导出面板勾选「显示性能面板」或
enableProfileStats = true(提审版本关闭)。 - 错误排查参考官方「开发错误调试与排查」,按开发者工具 / 真机 Android / iOS / PC Windows 分别看日志堆栈。
15.2 性能测试工具
- PerfDog(https://perfdog.qq.com/):真机 CPU / 内存 / FPS / 卡顿率,iOS 以 XcodeMemory 为准;
- 微信开发者工具自带性能监控面板;
- 小游戏云测试(自动化多机型);
- SoloPi、UWA 微信小游戏工具;
- Android Studio / Xcode Instruments。
自带的 PerfDog 小游戏内插件稳定性较差,不建议作为主手段。
15.3 云测试评分模型
云测试从 4 个维度 14 项指标打分,权重如下(评测标准更新时间 2025-06-18):
| 维度 | 指标 | 权重 |
|---|---|---|
| 启动性能 | 总启动耗时 | 0.34 |
| 游戏代码注入耗时 | 0.33 | |
| 首屏渲染耗时 | 0.33 | |
| 运行性能 | CPU 占比 | 0.25 |
| 内存峰值 | 0.25 | |
| FPS | 0.25 | |
| 卡顿率均值 | 0.25 | |
| 网络性能 | downloadFile / request / uploadFile / sendSocketMessage 失败率 | 各 0.25 |
| 兼容性 | 黑屏率 | 0.33 |
| JS 错误数 | 0.33 | |
| 启动失败 | 0.34 |
评分规则:达到「优秀范围」100 分,「平台建议范围」80 分,「达标范围」60 分,低于达标 0 分。
测试方法:
- 启动性能:录屏分帧,取 10 次平均;
- 运行性能:完整跑主流程对局 5~10 分钟,PerfDog 记录并上传,每机型测 3 组再取平均,内存峰值取最大值。
报告看点:内存峰值的 Top 机型分布、细分内存(total / graphics / native / private-other)、单机内存曲线拐点(定位异常场景)。
15.4 机型档位
平台把设备按 CPU / GPU / 内存分为高、中、低三档,覆盖 99% 以上用户。测试要覆盖三档,低档机是内存与卡顿的主要暴雷区。
十六、发布上线与现网监控
- 审核发布参考 MP 的小游戏接入指南自助完成。
- 版本更新与缓存:
- 每次发版更换 CDN 路径(如
Version_1→Version_2)是最省心的做法; - 不带 hash 的配置文件(
catalog.json/setting.json/ Manifest)设置no-cache; - 不要删除旧版本资源,否则老版本用户运行会报错。
- 每次发版更换 CDN 路径(如
- 关键监控项:启动耗时分布、内存峰值、内存 Crash 率、JS 错误数、黑屏率、下载与请求失败率。
- 上线后持续看微信公众平台的运行数据,出现异常优先对照云测试报告定位档位与机型。
- iOS 高性能模式需要在 MP 后台设置最低可用客户端版本,开关是可靠的(开发版可能强制进入,线上以该开关为准)。
十七、踩坑清单
| 问题 | 原因 | 处理 |
|---|---|---|
| AB 加载后 Shader 丢失 | Shader 没被打进包/被剥离 | Always Included Shaders 或 ShaderVariantCollection |
| 更新了资源但客户端还是旧的 | bundle 包名不变命中旧缓存 | AppendHashToAssetBundleName;配置文件不缓存;发版换 CDN 路径 |
| 不同电脑打出的 AB hash 不同 | Library 缓存差异 |
统一流水线出包,不要混用本地包 |
| 首包资源下载慢 | 没开 Brotli/gzip 或首包太大 | 强制开压缩;首包压到 3~5MB |
| iOS 内存过大闪退 | WASM 编译 + 纹理 + 首包常驻 | 代码分包 + 压缩纹理 + 减小首包 + 合理 UnityHeap |
| 离 1.5G 还很远就崩 | UnityHeap 预留不足或单帧分配尖刺 | 提高预留(但别超 500)、避免场景/AB 过大、避免单帧大量分配 |
| iOS 开高性能后前期很卡 | 启动阶段有 WASM 编译 CPU 高峰 | 单独优化启动段,不要只看平均帧率 |
| 切场景/关弹窗后内存不降 | AB 未卸载、对象池未清 | 设计卸载规则,切场景统一释放 |
音效报 Yc is not a constructor |
客户端/插件版本问题 | 升级微信与转换插件版本 |
UPM 接入 UnityWebSocket 报 missing function: WebSocketSetOnOpen |
低版本导出配置不全 | 升级到 v2.8.3+ |
| 剥离后运行期报缺失类型 | 反射/Activator 使用的类被裁 | link.xml 防剥离,预制体脚本单独保留 |
| 存档丢失 | WebGL 没有退出时自动保存 | 手动 PlayerPrefs.Save() + wx.onAppHide() |
| 首帧卡顿、白屏 | Start/Awake 同步阻塞 | 首帧逻辑最小化,配置分帧加载,配封面图 |
| 关闭按钮与微信按钮重叠 | 布局冲突 | 重新排布 UI 安全区与关闭按钮位置 |
十八、工时估算与排期
按中重度项目(已有 AB/AA 基础、无复杂网络同步)估算:
| 工作项 | 估时 |
|---|---|
| 资源加载方式改造(AA/AB + 分包 + 下载框架) | 2 周+ |
| 首包加载优化 | 4 天 |
| 同步代码改异步 | 3 天 |
| 本地数据存取修改 | 1 天 |
| 网络层修改 | 0 天(如无 socket 需求) |
| 微信 SDK 接入 | 1 周 |
| 原生 SDK 替换(广告/打点) | 3 天 |
| 资源压缩 / 降低包体 | 2 天 |
| WASM 代码分包 | 2 天 |
| 音频优化 | 2 天 |
| 功能调整(关闭按钮、支付等) | 1 天 |
汇总:功能开发约 6 周 3 天(1.5 个月+)。加上后期适配、打磨优化、联调、云测试与发版,总工期约 2 个月。
排期建议:
- 前两周集中打通「资源加载 + 真机跑通主流程」,这是风险最高的一段,必须尽早暴露问题;
- 第三周开始接入平台能力与 SDK,同时并行做代码剥离与资源瘦身;
- 第四到六周做启动、内存、音频优化;
- 最后两周做云测试、修复、发版与现网监控。
十九、参考资料
官方文档(国际版 Unity):
- 微信小游戏 Unity 适配解决方案
- 微信 SDK 安装(Package Manager / UnityPackage)
- Unity 游戏接入微信小游戏指南
- 提升游戏启动速度
- 优化内存
- 使用 Addressable Assets System 进行资源按需加载
- 使用 AssetBundle 进行资源按需加载
- 资源部署与缓存
- 资源缓存
- 使用预下载功能
- 代码分包
- 压缩纹理优化
- 音视频适配
- 网络通信适配
- iOS 高性能与高性能+模式
- 高性能+模式
- 推荐引擎版本
- MiniGameConfig.asset 配置文件说明
- 评测标准
- 测量指标(云测试评分)
- 使用云测服务检测内存
工具与仓库:
- minigame-unity-webgl-transform(官方文档与转换方案仓库)
- wasmsplit-ci(代码分包 CI 工具)
- Unity 国际版下载(Unity Hub)
- PerfDog
- AssetStudio
相关笔记:
评论