Error: 系统错误,错误码:80051,source
size 9424KB exceed max limit 4MB
相信 90% 的朋友第一次发布小游戏时,都会遇上首包限制的问题。
社区中有非常多关于如何减少首包大小的文章,但在 Cocos Creator 3.8 版本发布之后,引擎自带分包功能已经非常强大,在不改动引擎机制的情况下,就足以应对常见项目需求。
今天我们就以 Cocos Creator 自带的 Hello World 为例,一步步实现包体优化,最终效果如下:
项目 | 优化后 | 优化前 |
---|---|---|
总包大小 | 3.73MB | 9.22MB |
主包大小 | 2.14MB | 9.22MB |
分包大小 | 1.59MB | 9.22MB |
cocos-js | 1.68MB | 3.95MB |
包数量 | 2 | 1 |
具体情况可以点开下图查看:
接下来,我们从新建项目开始,一步步优化。
- 1.引擎裁剪
- 2.分包设置
- 3.资源引用剔除
- 4.大文件排查
- 5.WASM 分离
一、新建项目
-
新建项目面板,版本选择 Cocos Creator 3.8.2(建议用最新版本),模板选择 “Hello World”,即可创建带有素材的测试项目。
-
不做任何修改,发布到微信小游戏
-
用微信开发者工具打开,选择“代码依赖分析”,可以看到如下数据
我们可以看到它只有一个主包,总包大小已经 9.22 MB了,不做处理肯定是不行的。
二、引擎裁剪
定位到主菜单 -> 项目 -> 项目设置 -> 功能裁剪。
可以看到这里面提供了许多可选项。 我们移除 WebGL 2.0
、视频
、Webview
、Marionette 动画系统
、基础几何体
、地形
、光照探针
、2D 物理系统
、2D 粒子系统
、Tiled 地图
、Spine 动画
、Dragon Bones
、WebSocket
。
WebSocket 是针对原生平台的选项,小游戏和 Web 平台可以都去掉,不影响 WebSocket 使用
以上移模块除后,就是一个常见 3D 项目所需的基础模块了。
实际上,在这个例子中,
音频
、缓动系统
、3D 物理系统
、3D 粒子系统
因为并没有使用,都是可以移除的。但我们作为测试,尽可能保留大部分项目都需要用到的模块,才能符合商业项目需求。
总的来说就是:把不需要的模块都移除掉。
再次发布到微信小游戏,可以看到下面的结果:
项目 | 优化后 | 优化前 |
---|---|---|
总包大小 | 7.61MB | 9.22MB |
主包大小 | 7.61MB | 9.22MB |
cocos-js | 2.46 MB | 3.95MB |
一般的项目,都会带有许多游戏素材,光是裁剪肯定是不够的。接下来,我们通过分包设置,将游戏内容从主包里分离。
三、分包设置
3.1 调整目录
新建一个 game 文件夹,然后把所有其它文件都拖进去,最终目录结构如下:
拖进去后,如果发现 material 和 model 文件夹有残留,直接删除即可。
3.2 设置分包
选中 game 目录,做以下操作:
- 在右边的属性面板中,选择配置为 Bundle
- 点击“编辑”按钮,打开 Bundle 配置面板
- 勾选“单独配置”
- 微信小游戏一栏选择“小游戏分包”
- 点击“保存”
这样一来,分包就设置好了。
3.3 制作加载场景
分包不会自动加载,因此,我们需要制作一个场景用于加载。
- 在 assets 目录下新建场景起名为 “start”
- 在 assets 目录下新建一个脚本起名为 “Start”
- 在 Start.ts 中编写如下代码
assetManager.loadBundle("game",(err,bundle:AssetManager.Bundle)=>{
director.loadScene("main");
});
- 在 start 场景中,新建一个空节点,起名为 Start
- 将 Start.ts 组件挂上去
- 在 start 场景上放一个小 Logo,以避免什么都没有显示
最终的项目目录和画面可能如下:
在构建面板,将刚刚新建的 start 场景选择为启动场景,再次打包,可以在微信小游戏开发工具中看到如下数据:
项目 | 优化后 | 优化前 |
---|---|---|
总包大小 | 9.94MB | 7.61MB |
主包大小 | 4.02MB | 7.61MB |
分包大小 | 5.92MB | - |
cocos-js | 2.47 MB | 2.46MB |
可以看到,在优化后,虽然主包下降了,但总包大小上升了。
这里就得提到一个隐含的重要知识点了。
Cocos Creator 有以下几种包:
类型 | 优先级 | 用途 | 资源引用方式 |
---|---|---|---|
internal | 21 | 内置资源 | 仅打包已使用资源 |
resources | 8 | 内置 bundle | 打包所有资源 |
main | 7 | 主包 | 仅打包已使用资源 |
bundle | 1+ | 普通分包 | 打包所有资源 |
Bundle 优先级
assets 目录下的资源,如果不处于 bundle 中( resources 是一个内置的bundle,行为和普通 bundle 一致)时,则仅会将使用过的资源入对应的bundle包。
- 当被多个不同优先级的包同时引用时,打入优先级最高的那个包。
- 当被多个相同优先级的包同时引用时,会每个包都打入。
对于内置 bundle,internal, resources, main 来说,优先级还决定了加载顺序。可以看出, resources 会比 main 先加载。 因此,应该尽量避免在 resources 目录放置过多内容,大型项目可以直接避免使用 resources 目录。
四、资源引用剔除
有了以上的 Bundle 和优先级相关的知识后,我们可以知道,Bundle 中只需要放以下几个内容:
- 需要的场景
- 需要动态加载的素材(Prefab、图片、动画、音频等)
对于不需要动态加载的,我们通过场景引用,引擎会自动帮我们处理好资源关系。
接下来,我们新建一个 res 目录,并且把 game 目录下除 scene 以外的文件夹移动过去,得到的目录结构如下:
项目 | 优化后 | 优化前 |
---|---|---|
总包大小 | 8.50MB | 9.94MB |
主包大小 | 4.02MB | 4.02MB |
分包大小 | 4.48MB | 5.92 |
cocos-js | 2.47 MB | 2.47MB |
可以发现,包体大小还是不够理想,那么我们就要来具体分析占用包体较大的文件。
主包排查
通过查看分布图,我们可以发现,主包除了代码外为,assets 目录还占用了 1.36 MB。
而我们放的 logo.jpg 只有 41 KB,显示是超出预期的。
定位到对应文件夹后,发现是一堆天空盒贴图:
这就好办了!
由于我们的 start 场景不需要显示天空盒,所以只需要去除这个引用就行。
-
打开 start 场景
-
关闭场景面板上的 Skybox,移除场景面板上的天空盒引用
-
Main Camera 的 ClearFlags 设置为 SOLID_COLOR
再次发布版本,可以看到主包数据变化:
项目 | 优化后 | 优化前 |
---|---|---|
总包大小 | 7.66MB | 8.50MB |
主包大小 | 3.19MB | 4.02MB |
分包大小 | 4.48MB | 4.48MB |
cocos-js | 2.47 MB | 2.47MB |
可以看到,最终我们得到了 7.66 MB 的总包大小,并且主包控制在了 3.19 MB。已经可以满足我们的需求。
但这并非结束,甚至,优化才刚刚开始。
大文件排查
通过依赖分析图,我们可以看到子包里的 native 有 3.99 MB 占用。
native 下一般是图片为主,我们定位到目录中可以看到是 Hello World 中使用的天空盒。
在 Cocos Creator 中查看,可以看到它有 4096 x 3072 这么大
对于这个天空盒,根本不需要这么高的精度,通过图片工具,直接将它改为 1024 x 768。
同时,我们发现 seafloor,stone 等分辨率,是可以缩小一倍的。
虽然可以使用 tinypng 等工具对图片进行压缩,但建议在压缩之前调整好合理的分辨率,能够降低内存开销。
将这些素材处理好之后,再次打包。
项目 | 优化后 | 优化前 |
---|---|---|
总包大小 | 4.77MB | 7.66MB |
主包大小 | 3.19MB | 3.19MB |
分包大小 | 1.59MB | 2.18MB |
cocos-js | 2.47 MB | 2.47MB |
WASM 分离
虽然主包已经控制在了 3.19 MB。但如果我们要同时开启 Spine,Box2D 等功能,或者要在 start 场景里加载一些资源, 0.81 MB 的空间还是略显拮据。
因此,引擎在小游戏打包时,提供了 WASM 分离功能。 所有开启了 WASM 的模块,都会在打包时,放入子包,以减少主包占用。
勾选后再次打包,可以看到:
项目 | 优化后 | 优化前 |
---|---|---|
总包大小 | 5.39MB | 5.37MB |
主包大小 | 2.70MB | 3.19MB |
分包1大小 | 1.59MB | 1.59MB |
分包2大小 | 463KB | - |
分包3大小 | 57KB | - |
cocos-js | 1.99 MB | 2.47MB |
3D 项目极限尝试
其实有许多 3D 项目和这个 HelloWorld 一样,不需要物理,也不需要 3D 粒子系统,那么可能是如下数据:
2D 项目极限尝试
如果你是一个 2D 项目,需要 Spine,但是不需要物理、2D 粒子等。
我们可以做到主包 1.94 MB,cocos-js 1.64 MB。
如果觉得默认的配置还不够满意,可以自己进一步裁剪引擎,达到最小化功能。
附1:分包常用知识
- 小游戏分包可以包含代码
- 远程包不可以包含代码,如果将包含代码的包设置为远程包,则代码会自动被抽离
- 非 ZIP 格式的分包,在调用 assetManager.loadBundle 时,只会加载资源列表,不会加载所有资源
- ZIP 格式的分包,会在第一次加载完成后解压,后面的加载请求会从缓存中读取
- 分包内的代码,在加载完成后自动装载执行,但是卸载分包不会卸载代码
附2:小游戏分包机制
随着小游戏的玩法越来越丰富,开发者对于扩大包大小的需求越来越强烈,所以众多小游戏平台都推出了分包加载这一个功能。
所谓的分包加载,即把游戏内容按一定规则拆分这几包,在首次启动时先下载必要的包,这个必要的包我们称为「主包」,开发者可以在主包内触发其它分包的下载,从而把首次启动的下载耗时分散到游戏运行中。
附3:分包限制
目前小游戏分包大小有以下限制:
- 整个小游戏所有主包+分包大小不超过 20M(开通虚拟支付后的小游戏不超过30M)
- 主包不超过 4M
- 单个普通分包不限制大小,但受总包大小限制
- 单个独立分包不超过 4M
独立分包是小游戏中一种特殊类型的分包,可以独立于主包和其他分包运行,对于一些大厅子游戏玩法的游戏很好用,今天暂不讨论。
希望这篇文章能够帮助到大家!
回头这篇文章会以“最佳实践”纳入官方文档,也希望各位开发者提交最佳实践相关文章 PR 到官方文档仓库。