Unity 转微信小游戏指南

Unity 性能优化 构建发布 内存优化 跨平台

这份指南面向已有 国际版 Unity 手游、希望低成本迁移到微信小游戏的项目。主线参考微信小游戏官方的「Unity 适配解决方案」与「快适配」文档,并结合一次真实项目(Unity 2021.3、中重度模拟经营类)从评估到上线的实践。全文按国际版 Unity 工具链书写,不涉及国内特供引擎与相关云服务。核心结论是:能转,但不是「一键转换」——核心玩法代码基本不用重写,但资源加载、启动性能、内存峰值这三件事必须重做。

目录

一、先看结论

  • 技术上可行:微信适配方案基于 WebAssembly,支持国际版 Unity 2018~2022,大部分第三方插件可以复用,核心逻辑不用重写。
  • 成本主要在工程改造,不在玩法代码:资源加载方式、启动流程、内存模型、平台 SDK 四块需要系统改造。
  • 优先级不能颠倒:先跑通(阶段二)→ 再压首包和启动(阶段四)→ 再压内存峰值 → 最后才是画面与体验打磨。一上来就优化画质,很容易在内存和启动上翻车。
  • 内存是生死线:iOS 低档机超过 1G、中高档机超过 1.4G 就有大概率被系统 OOM 杀掉重启,这不是「卡不卡」的问题,是「活不活」的问题。
  • 必须改掉 Resources 主路径:国际版 Unity 没有「资源自动流式托管」捷径,首包与内存要压下来,只能走 Addressable / AssetBundle + 自建 CDN。
  • 收益是真实的:即点即玩、免安装、微信买量与社交裂变,这些都是原生 App 拿不到的。

二、平台差异与性能预算

先理解差距,再定预算,最后才是动手优化。

2.1 内存差异

  1. WASM 编译内存:导出目录 webgl/Build/xxx.code.unityweb(或 code.wasm)本身就有几十 MB,浏览器内核编译执行时还会产生更多内存,加上缓存与 JIT 优化,总体大致是 Wasm 文件大小的 10 倍左右。这块开销原生 App 完全没有。
  2. Emscripten 文件系统:WebGL 出于沙盒机制无法访问本地文件系统,Emscripten 用 JS + IndexedDB 模拟了一套文件系统。Wasm 访问 JS 层,JS 层再定期与 IndexedDB 同步,所以 JS 中始终存有所有文件的一份 copy。这也是为什么官方反复强调「首包资源与 AssetBundle 都不要用 Unity 自带的文件 Cache」。
  3. 内存是「只增不减 + 有碎片」的: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 左右。

微信小游戏 Unity 内存构成与峰值红线

官方建议的内存峰值红线:

平台 低档机 中高档机
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 个

三、转换路线:五个阶段

官方把接入拆成五个阶段,建议直接按这个节奏推进,每个阶段都有明确的准入条件,避免「边做边发现方案不行」。

Unity 转微信小游戏:五阶段转换路线

阶段 名称 核心工作 交付物 / 放行条件
兼容性评估 引擎版本、渲染管线、网络协议、第三方插件、原生 SDK 盘点 可行性结论 + 方案选型
项目转换 安装插件、WebGL 导出、转换小游戏、资源上传 CDN 真机能跑通主流程
平台能力接入 WX SDK(登录/广告/分享/开放数据域/存储)、屏幕与输入法适配、安全域名 平台功能可用,原生 SDK 已下线
体验调优 首包与首帧、WASM 分包、压缩纹理、AB 卸载、内存峰值、音频 各项指标进入预算表
发布上线 审核发布、云测试验收、现网监控与版本更新 上线并有人盯数据

四、资源加载方案选型

这是整个迁移里最贵、也最关键的决策。国际版 Unity 没有国内特供的「Resources 自动远端流式」能力,主线只能是 Addressable / AssetBundle + 自建对象存储与 CDN。先定方案再动工。

4.1 Addressable(推荐)

用 Addressable Assets System(AA)做按需加载、依赖解析与更新。

4.2 AssetBundle

直接用 AssetBundle(AB)做分包下载与加载,自行封装更新与依赖。

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 / 转换工具即可,按官方安装指引操作。

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 图)。

首包原则:

  1. 只保留 Splash / Loading 场景,不要勾选任何其他场景;
  2. 不要把字体打进首包(字体压缩率极低),中文用 2~3MB 以内的精简字体;
  3. 不要往 Resources 放资源,该目录会被无条件打进首包;
  4. 首包压缩后体积以 3MB 左右最佳,不超过 5MB。

6.2 Addressable 改造

Addressable 是官方推荐的迁移路径,最小改造量的做法:

  1. Resources.LoadAddressables.LoadAssetAsync<T>
  2. 直接 Instantiate(prefab)AssetReference.InstantiateAsync(),否则预制体及其全部依赖必须在场景加载前就绪,会严重拖慢初始化;
  3. 每个场景单独作为一个 Addressable Group,用 Addressables.LoadSceneAsync 动态加载;
  4. 用 Tools → Analyze 检查冗余,必要时「Check Duplicate Bundle Dependencies → Fix Selected Rules」自动消除重复依赖;
  5. iOS 内存压力大时,把 Provider 换成 WXAssetBundleProviderWXAssetBundleProvider.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.LoadFromCacheOrDownloadLoadFromFile 等缓存 API,直接用 UnityWebRequestAssetBundle 按需异步加载,缓存的判断与淘汰由适配层负责。

6.4 Shader 丢失

AB 资源加载后 Shader 丢失是高频问题,两种解法:

  1. 把用到但没被引用的 Shader 放入 Always Included Shaders
  2. 收集并设置 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 与服务端注意事项

  1. .txt 后缀(首资源包)开启 Brotli 或 gzip
  2. 资源下载并发数为 10,超过自动排队;
  3. 单个请求最大 100MB、超时默认 60s,但建议单文件控制在 2~5MB
  4. 网络与安全域名、跨域、SSL 参考官方「网络通信适配」文档。

七、首包与启动优化

7.1 先量化,再优化

unity-namespace.js 里的 hideTimeLogModal 设为 false,就能看到启动 timelog。启动主要由三部分决定:

  1. 首包资源下载:资源越大越慢,绝大部分玩家下载速度约 2MB/s,还有不少 <300KB/s 的低速用户;
  2. WASM 代码下载与编译:代码包影响着下载时长与初始化编译时间,转换工具会把它 br 压缩到原 code 包的约 20%;
  3. 引擎初始化与首帧逻辑:典型 3~6s,且这段是 CPU 密集、网络空闲,正是预下载的黄金窗口。

7.2 首包瘦身

  • AssetStudio 检查 data 首包与 AB 的资源内容,找出错误打包和冗余:https://github.com/Perfare/AssetStudio
  • BuildReportTool 看每次构建各资源的占用,用 Asset Hunter 清理无用资源。
  • 首资源包也可以勾选「压缩首包资源」(Brotli),代价是首次启动可能多约 200ms,主要用于「小游戏包内加载」时省包体。

7.3 WASM 代码分包

把原来一个 wasm 拆成「启动主包 + 延迟加载子包」,可以同时降低启动下载时间、编译时间和运行内存。

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)、JS GameGlobal.manager.setPreloadList(list) 动态设置;
  • 并发:引擎初始化前默认 10 个并发,初始化后降为 1 个,可用 WX.PreloadConcurrent / GameGlobal.manager.setConcurrent 调整;
  • 原则:
    1. 预下载总体积控制在 3~5MB;
    2. 文件数量 ≤ 10 个;
    3. 必须是插件会自动缓存的文件,否则下载无效;
    4. 按优先级排序,越重要的越靠前;
    5. 预下载 URL 必须和后续加载 URL 完全一致,否则命中不了缓存。

7.5 首帧逻辑

  1. MonoBehaviour 的首帧 Awake / Start 逻辑要尽可能少,优先把画面呈现出来;
  2. 初始场景不宜过大,能显示 Splash 即可;
  3. 后续配置与主场景加载要分帧,切勿在 Start / Awake 里同步阻塞
  4. 首帧耗时可以用 Android CPU Profiler 逐帧定位。

7.6 启动封面

Unity WebGL 启动需要时间,微信支持配置封面图/视频作为过渡,可自定义封面内容、加载文案样式、进度条样式、自动隐藏时机。这是留存的第一道防线,不要留默认白屏。

八、内存优化

内存优化的思路是:先测准峰值,再逐个消掉大头。官方给出了最容易出问题的几个位置,基本按这个顺序排查。

8.1 设置合理的 UnityHeap 预留

  • 该值只表示对 UnityHeap 峰值的预留,避免运行中扩容产生尖刺;
  • 取值方法:
    1. 导出面板勾选「显示性能面板」,或把 unity-namespace.jsenableProfileStats 打开(提审版本必须关掉);
    2. 游戏跑一段时间,观察 DynamicMemory 峰值;
    3. UnityHeap = DynamicMemory + 少量静态内存(通常 <10MB),预留比峰值多 50~100MB。
  • 参考值:超休闲 256 / 中度(模拟经营、卡牌成长)496 / 重度(SLG、MMO)768;
  • 不要贪大
    • UnityHeap ≥ 1024MB 时,大部分设备会直接启动失败;
    • UnityHeap ≥ 500MB 时,32 位微信(约 5% 用户)与 iOS 普通模式大概率启动失败。

8.2 降低 WASM 编译内存

  • 代码分包工具能把编译内存降低 50% 以上,这是单项收益最大的优化;
  • 删除多余插件,减少不必要的引擎模块(物理、数据统计等)。

8.3 减少 GPU 显存

  1. 用压缩纹理(ASTC)替代 RGBA/DXT,既降显存也降运行时解压开销;
  2. Unity 2021 及以上直接用引擎 ASTC;2018~2020 必须用微信压缩纹理工具;
  3. 关闭 HDR:标准管线在 Graphics Settings → tier2 取消 Use HDR,URP 在 renderer 配置里取消;
  4. 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 内存排查步骤

  1. 测试内存时不要开 development、profilingmem;
  2. 必须用代码分包 + 压缩纹理;
  3. 用 PerfDog 或 Instruments 看 WebContent 进程内存,安全峰值 1.2~1.3G;
  4. 若离 1.5G 上限还很远就崩溃,优先检查 UnityHeap 预留是否足够;
  5. 打开性能面板看 DynamicMemory 峰值,建议不要超过 500M;
  6. 用 PerfDog 看 Android 的 GL / Gfx 显存,显存压力大就开高性能+;
  7. 以上都做完还有问题,带着详细数据找平台侧分析。

九、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 里设为 NoneExplicitly 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 startrender 字段为 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
插件 SetDataCDNSetPreloadListPreloadConcurrent
其他 InitSDKCanIUseSetDevicePixelRatioReportGameStartHideLoadingPageOnLaunchProgress

接入顺序建议:

  1. 登录:先打通接口与后端验证,再补全功能;
  2. 广告:激励视频是主要变现,必须与打点、存档、发奖逻辑联调,重点测「看完/中途退出/重复观看」;
  3. 打点上报:替换原生的打点 SDK,并补上启动留存等关键埋点(官方 Unity Loader 已提供基础上报,游戏内关键帧需要自己上报);
  4. 分享与排行榜:涉及开放数据域,注意渲染层级与权限;
  5. 设备能力:存储、震动、字体。

原生 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 会把「看起来没人用」的类型裁掉,以下情况必须做防剥离:

  1. 预制体上挂载的脚本
  2. 通过反射调用的类与成员
  3. Activator.CreateInstance 动态创建的类
  4. 注册表/工厂模式里按名字创建的类。

做法:配置 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.jshideTimeLogModal = false
  • 性能面板:导出面板勾选「显示性能面板」或 enableProfileStats = true提审版本关闭)。
  • 错误排查参考官方「开发错误调试与排查」,按开发者工具 / 真机 Android / iOS / PC Windows 分别看日志堆栈。

15.2 性能测试工具

  • PerfDoghttps://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_1Version_2)是最省心的做法;
    • 不带 hash 的配置文件(catalog.json / setting.json / Manifest)设置 no-cache
    • 不要删除旧版本资源,否则老版本用户运行会报错。
  • 关键监控项:启动耗时分布、内存峰值、内存 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 个月。

排期建议:

  1. 前两周集中打通「资源加载 + 真机跑通主流程」,这是风险最高的一段,必须尽早暴露问题;
  2. 第三周开始接入平台能力与 SDK,同时并行做代码剥离与资源瘦身;
  3. 第四到六周做启动、内存、音频优化;
  4. 最后两周做云测试、修复、发版与现网监控。

十九、参考资料

官方文档(国际版 Unity):

工具与仓库:

相关笔记:

评论