17.3 Cocos 对照:构建面板与多平台发布
前两节我们用 mini2d 走完了「打包 → 量体积 → 发三端」的全流程,靠的是 vite.config.ts 加几个脚本。Cocos Creator 3.8 做的是同一件事,只不过全部收进一个 GUI 面板:选平台、点构建,剩下的压缩、哈希、分包由编辑器代劳。这一节把那个面板摊开对照——它替我们做了哪些决定,又要求我们接受哪些默认约定。
本节属 L3:以下构建面板、工程文件与产物结构均需 Cocos Creator 3.8 编辑器操作,本节不可命令行复现。 打开面板、勾选选项、点构建,都发生在图形界面里,没有任何 CLI 等价物。唯一的例外是 17.3.6 的运行时 API 片段(L2),每个
cc符号都已对cc.d.ts逐个核对,可用tsc --noEmit做类型检查。
17.3.1 构建面板总览(L3)
在编辑器里点顶部菜单 项目 → 构建发布,会打开一个停靠面板。打开后会看到什么:左边一列是已创建的构建任务,右边是选中任务的一张长表单,底部固定三个按钮。
- 左侧构建任务列表:每个平台一个任务,可以同时存在多个(web-mobile、wechatgame、native 各一份),点某个任务编辑它的参数。任务本身会保存在工程里,下次打开编辑器仍在。
- 右侧参数表单:分「通用」与「平台专属」两大块。通用块是平台、发布路径、起始场景、资源选项;平台专属块随所选平台变化——选微信小游戏会多出 AppID 与分包相关项,选原生会多出方向与渲染后端。
- 底部三个按钮:构建(只生成产物)、构建并运行(构建后在对应环境打开)、预览(走内置预览服务器,不产出正式产物)。
第一次打开面板,绝大多数选项都带着编辑器给的默认值。默认值对「能跑起来」是够的,对「体积达标」通常不够——这正是要逐条看的原因。
还有一点新手常困惑:面板里的构建任务是可复用的。你为微信小游戏调好一套参数后,它随工程一起保存,下次打开直接点「构建」即可,不必重配。这既省事,也是个协作风险点——参数存在本地工程文件里,若没提交进版本库,换台机器就得从头配一遍。所以「构建配置算不算源码」这个问题,答案是算:它和 .ts 一样应该进版本库。
17.3.2 关键选项逐条(L3)
把和我们 Vite 配置一一对应的选项挑出来:
| 面板选项 | 作用 | 对应 mini2d 的做法 |
|---|---|---|
| 平台(Platform) | 决定产物形态与目标运行时 | 无(我们一次只发 H5) |
| 发布路径(Build Path) | 产物输出目录,默认 build/<平台名> | build.outDir |
| 起始场景 | 打包进首屏的入口场景 | rollupOptions.input 入口 |
| 资源压缩(压缩纹理) | 按平台压缩纹理格式 | 我们自己压 WebP |
| MD5 Cache | 文件名加内容哈希 | entryFileNames: [hash] |
| 配置为 Bundle | 目录级分包 | manualChunks + 动态 import |
| 调试模式 / Source Map | 是否保留可调试信息 | build.sourcemap |
逐条说明三个最关键的:
MD5 Cache。 勾选后,产物里的资源文件名会带一段内容哈希,和上一节我们自己配的 [hash] 完全同构。它的作用是让 CDN 可以长缓存——内容变了哈希变,玩家自动拉新。上线产物必须勾,调试时可以关掉以便对照文件。
资源压缩(压缩纹理)。 编辑器允许你为不同平台指定纹理压缩格式(如 Android 用 ETC2、iOS 用 ASTC)。这和我们在 Vite 里把 PNG 换成 WebP 是同一目的——减小体积、降低显存占用。区别是 Cocos 会在构建期按平台生成多套纹理,产物因此按平台分开;你勾几个平台,就生成几份纹理。
配置为 Bundle。 这是 Cocos 的分包机制。你在资源管理器里右键某个文件夹,选「配置为 Bundle」,它就变成一个独立 Bundle;构建时该文件夹被打成独立包,运行时用 assetManager.loadBundle('名字') 按需加载。对照我们上一节做的 manualChunks 与动态 import()——思想一致:把不急着用的东西挪出首包。
另外两个容易忽略的选项:
起始场景(Start Scene)。 它决定玩家打开游戏后先进哪个场景。只有被引用到的场景才会被打进产物——没在起始场景或任何 Bundle 里出现的场景,构建时会被当作「用不到」而剔除。这是个双刃剑:好处是产物精简,坏处是你新加的场景若忘了挂进构建范围,运行时 director.loadScene('新场景') 会直接报「场景不存在」。排查这类问题,第一件事就是回构建面板确认场景在不在。
调试模式 / Source Map。 打开后产物会保留可读的调试信息,崩溃栈能映射回源码;代价是体积增大、代码可被轻易阅读。上线必关,联调才开。这和 mini2d 里 build.sourcemap 的取舍完全一样。
17.3.3 平台专属配置(L3)
通用选项之外,每个平台还有自己的一组参数。挑两个差异最大的看:
微信小游戏。 面板里会出现:
- AppID:小游戏后台申请的账号标识,构建时写入产物,缺了没法上传。
- 分包(Subpackages):把标记为 Bundle 的目录分配进不同分包,对应
game.json的subpackages。 - 远程服务器地址:远程资源 CDN 的根地址,运行时从它拼出资源 URL。
- 开放数据域:排行榜等需要独立上下文的能力,单独一份代码与配置。
原生。 面板里会出现:
- 方向(Orientation):横屏 / 竖屏 / 自适应,写入原生工程的配置。
- 渲染后端:WebGL / 原生渲染(可选),决定 Canvas 之上那层怎么画。
- 包名与版本:Android 的
applicationId、iOS 的 Bundle Identifier。 - 目标 API 等级:Android 最低 SDK、iOS 最低版本。
这些参数有个共同点:它们不是「构建选项」,而是「发布元数据」。也就是说,改它们往往意味着改的是「发给谁、发到哪个账号」,而不是「产物长什么样」。团队协作时尤其要注意——这类元数据最好集中管理,避免每个人的本地构建把包名或 AppID 带偏。
Web 平台。 选项最少,主要有两项:
- 资源服务器地址(Remote Server Address):如果资源放在 CDN 而不是打进包,构建时会把这个前缀写进产物,运行时据此拼 URL。这对应 17.2 里「大资源走 CDN」的做法。
- 内联所有 SpriteFrame / 调试模式:控制资源是否内联、是否保留调试信息,与
mini2d的assetsInlineLimit、sourcemap一一对应。
对照下来会发现:平台越「封闭」(小游戏、原生),专属选项越多;越「开放」(Web),选项越少。因为封闭平台有更多平台侧的规则要满足,而 Web 的自由度本就最高。
17.3.4 各平台产物结构(L3)
点完构建,产物落在 build/<平台名>/ 下。三个平台的目录形状不一样,认一下就不会找错文件:
build/
├── web-mobile/ # H5
│ ├── index.html # 入口,指向下面的 assets
│ ├── assets/
│ │ ├── main/ # 首包资源与代码
│ │ ├── internal/ # 引擎内置资源
│ │ └── resources/ # 配置为 Bundle 的资源
│ └── cocos-js/ # 引擎运行时(CC 引擎代码)
├── wechatgame/ # 微信小游戏
│ ├── game.js # 小游戏入口
│ ├── game.json # 小游戏配置(含分包声明)
│ ├── assets/ # 与 H5 类似的资源结构
│ └── cocos-js/
└── native/ # 原生
├── assets/ # 打进 App 的本地资源
└── proj/ # 原生工程壳(Android/iOS)
三处值得注意:
cocos-js/是引擎本身。它是产物里体积占比不小的固定开销,和你的业务代码无关。理解这点,就不会把「引擎 1 MB」误算成「我的代码太大」。wechatgame/game.json由编辑器生成,里面的subpackages字段来自你「配置为 Bundle」的那些文件夹——上一节我们手写的那份 JSON,在 Cocos 里是自动产出的。打开它大概长这样:
{
"deviceOrientation": "landscape",
"showStatusBar": false,
"subpackages": [
{ "name": "levels-a", "root": "assets/levels-a" },
{ "name": "levels-b", "root": "assets/levels-b" }
]
}
native/proj/是原生工程,构建只生成它,真正打包成 App 还要用 Xcode / Android Studio 再编一次。也就是说 Cocos 的原生发布是两段式:先出资源与工程壳,再交给原生工具链。
顺带记一个排查技巧:若构建后某个资源「运行时找不到」,先在 build/<平台>/assets/ 下按名字搜一遍。Cocos 会按资源类型分目录存放,图片与其 .json 描述文件是分开的,找到文件却仍报 404,多半是路径大小写或 CDN 前缀配错——这与 17.2 里 H5 的 base 路径问题是同一类。
17.3.5 构建报告与体积(L3)
构建完成后,编辑器会输出一份构建报告,列出各包、各资源的体积明细。打开后会看到什么:一张按包分组的列表,每行给资源名、类型与字节数,末尾有合计。这份报告就是上一节我们自写 analyze-dist.ts 想得到的东西——Cocos 直接给你。
对照起来,能力各有侧重:
| 能力 | mini2d 的 analyze-dist.ts | Cocos 构建报告 |
|---|---|---|
| 按类别汇总 | ✓(我们自己定义类别) | 按包 / 资源类型 |
| gzip 预估 | ✓(zlib.gzipSync) | 需自行另算 |
| 预算门禁 | ✓(exit 1 进 CI) | 报告只展示,不拦截 |
| 覆盖引擎体积 | 无(引擎是我们自己写的) | 有(cocos-js 单列) |
差异很清楚:Cocos 的报告更适合「看」,我们的脚本更适合「拦」。实践中常见的做法是两者结合——用编辑器报告定位大头,再把关键阈值写成脚本放进 CI。记住上一节的结论:超标时先看是哪个类别,再决定压资源还是拆代码,别盲目删功能。
最后提醒一个数字口径问题:构建报告里的体积通常是原始体积,而玩家实际下载的是 gzip / brotli 之后的大小,两者能差出一半以上。别拿报告里的数字直接和预算红线比——先按 17.1 的 gzipSync 换算一遍,或者看编辑器是否提供压缩后预览。口径不统一,讨论体积就是鸡同鸭讲。
17.3.6 运行时:平台判定与 Bundle 加载(L2)
产物结构是静态的,运行时要根据平台做不同的事——这正是上一节我们写平台抽象层的原因。Cocos 把平台信息与资源管理都放进了 cc 模块,下面这段可以 tsc --noEmit 类型检查:
import { sys, assetManager, resources, director, game } from 'cc';
// 平台判定:sys.platform 是一个枚举,另有 isNative / isBrowser 布尔位
export function platformTag(): string {
if (sys.platform === sys.Platform.WECHAT_GAME) return 'wechat';
if (sys.isNative) return 'native';
if (sys.isBrowser) return 'h5';
return 'unknown';
}
// resources 是随包发布的默认 Bundle,适合放首屏必需资源
export function preloadFirstScreen(onDone: () => void): void {
resources.preload('audio/bgm', () => onDone());
}
// 自定义 Bundle:构建面板里「配置为 Bundle」的文件夹,按名加载
export function loadLevelPack(name: string, onDone: (err: Error | null) => void): void {
assetManager.loadBundle(name, (err, bundle) => {
if (err) {
onDone(err);
return;
}
bundle.preload('config/level', () => onDone(null));
});
}
// 帧率与场景切换,对应 mini2d 的固定步长循环与场景栈
export function boot(): void {
game.frameRate = 60;
director.loadScene('main');
}
逐点对照:
sys.platform/sys.isNative/sys.isBrowser就是上一节detectPlatform()的引擎版。sys.Platform是一个字符串枚举,WECHAT_GAME、ANDROID、IOS、MOBILE_BROWSER等值都在cc.d.ts里列着——不要凭记忆写平台名,先 grep 一遍。resources是一个特殊的预置 Bundle,等价于我们「随包发布、不做分包」的那部分资源;自定义 Bundle 则用assetManager.loadBundle(name, cb)加载,回调拿到bundle后即可bundle.preload/bundle.load。game.frameRate与director.loadScene对应我们第 4 章的帧率控制与第 12 章的场景/状态切换,是同一层抽象。
这套 API 的存在,恰好证明了「平台抽象层」不是多此一举:Cocos 把三端差异收进了 sys 与 assetManager 两个入口,我们的 Platform 接口做的是同一件事,只是规模小得多。
17.3.7 与 mini2d 构建流程对照
把两套流程并排,能看清 Cocos 的抽象层次:
| 阶段 | mini2d(Vite + 脚本) | Cocos Creator 3.8(面板 + 引擎) |
|---|---|---|
| 拆包 | manualChunks + 动态 import() | 「配置为 Bundle」+ loadBundle |
| 哈希 | entryFileNames: [hash] | 勾选 MD5 Cache |
| 压缩纹理 | 手动转 WebP | 按平台生成 ETC2 / ASTC |
| 体积统计 | 自写 analyze-dist.ts | 构建报告面板 + 手动核对 |
| 发布 H5 | 拷 dist/ 到 CDN | 构建 web-mobile,拷 build/web-mobile/ |
| 发微信 | 手写 game.json 的 subpackages | 编辑器生成 game.json |
| 发原生 | 无(不在 scope 内) | 构建 native,再用 Xcode/AS 打包 |
一句话总结:Cocos 把「配置项」变成了「编辑器里的勾选与右键菜单」。好处是上手快、不容易漏配;代价是这些选项藏在 GUI 里,团队协作时难以进版本库审查——所以 Cocos 才把关键配置也落进 settings/ 下的工程文件,让它们能被 diff。理解 mini2d 那套显式配置,再回头看面板,就知道每个勾选背后到底改了什么。
17.3.8 从构建到上架(L3)
构建产出 build/<平台>/ 之后,离「玩家能玩到」还有一段路。三端的后半程各不相同:
- H5:把
build/web-mobile/整目录拷到静态服务器或对象存储,配上 17.2 那套缓存策略即可。整个流程可以在 CI 里全自动完成。 - 微信小游戏:用微信开发者工具打开
build/wechatgame/,本地真机预览无误后上传,再在小游戏后台提交审核。审核会检查包体是否超限、是否有诱导分享等,包体超标是最常见的打回原因。 - 原生:用 Xcode / Android Studio 打开
build/native/proj/,配置签名证书后分别出.ipa与.apk/.aab,再提交 App Store 与各安卓商店。商店审核比小游戏更严,隐私政策、权限说明都要备齐。
这段流程里,Cocos 只负责「前半程」(生成产物与工程壳),「后半程」完全交给各平台自己的工具链与后台。这也解释了为什么「一次构建、三端上架」在实践中并不轻松——构建可以统一,上架永远因平台而异。mini2d 与 Cocos 在这一点上没有区别,差别只在产物从哪来。
还有一条容易被忽略的纪律:版本号要在构建前就定好。三端各自有自己的版本字段(小游戏后台、App Store、安卓商店),若三处对不上,排查线上问题时根本分不清玩家装的是哪一版。建议把「游戏版本」作为唯一事实来源,构建时同步写进三端的元数据——mini2d 里我们把它写进一个 version.json 打进产物,Cocos 则在构建面板的版本字段里填。同一个数字,两个世界。
17.3.9 构建缓存与二次构建(L3)
一个工程改一行代码后重新构建,耗时往往远低于首次。打开构建日志会看到什么:一部分资源被标为「跳过(未变更)」,只有改动过的场景与脚本被重新处理。这就是构建缓存——编辑器记录了每个资源的导入结果,内容没变就不重算。
缓存对日常开发的意义有三点:
- 改一处只重编一处:改一个脚本,不会触发全量纹理压缩,迭代快。
- 切换平台会重建缓存:从 web-mobile 切到 wechatgame,纹理格式与产物结构都不同,编辑器会重新生成对应平台的资源,首次切平台必然慢。
- 缓存出问题时的解法:若产物行为诡异、疑似缓存不一致,编辑器提供「清理构建缓存」的入口,重建一次即可排除。这和
mini2d里删掉node_modules/.vite再构建是同一类操作。
理解缓存后,就能解释一个常见现象:同样的源码,换台机器构建,产物哈希可能不同。因为缓存状态与构建环境不同,某些中间产物会带环境信息。这也是为什么「发布产物应由同一台构建机产出」是一条值得遵守的纪律——它保证了产物的可复现性,正好呼应第 16 章讲过的「确定性」。
17.3.10 小结
第 17 章到此把《方块冲刺》从源码推到了可发布状态:先量体积、定预算,再按 H5 / 微信小游戏 / 原生三端的约束分别发布,最后用 Cocos 的构建面板做了一次完整对照。你手上现在有两条等价的发布路径——一条是可控、可进 CI 的脚本管线,一条是开箱即用的编辑器面板。哪条更合适,取决于团队是否愿意为「可审查」付出额外的配置成本。
接下来,第一卷进入最后一章:把 mini2d 与 Cocos Creator 这两套实现放到一起,逐模块对照,再给出一份从 mini2d 迁移到 Cocos 的清单。下一节先看「同一款游戏的两套实现」。
阅读导航:上一节:17.2 多端发布:H5 / 微信小游戏 / 原生 · 下一节:18.1 同一款游戏的两套实现对照 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。