Web 与原生构建差异

4.1 三端到底差在哪

Cocos Creator 3.x 是双内核架构:C++ 内核跑原生平台,TS 内核跑 Web 和小游戏平台。所以「一份代码三端跑」这个说法成立,但成立的前提是你别在代码里假设运行环境的形状。三端的差异集中在六个方面。

维度 Web(Web Mobile / Web Desktop) 微信小游戏 原生 Android / iOS
包体限制 无平台硬限制,受 CDN 与带宽影响 主包 4M,主包+分包总和 30M(以微信公告为准) 受应用商店限制
加载方式 浏览器拉包 + HTTP 缓存 一次性加载包内资源,再启动页面 包内资源直接读盘
文件系统 只有浏览器存储,资源靠 CDN 有文件系统,引擎自管缓存(assetManager.cacheManager) 有可写目录,jsb.fileUtils 搜索路径
音频 Web Audio,受浏览器自动播放策略限制 走微信宿主能力 走系统音频栈
输入 鼠标 + 键盘 + 触摸 触摸为主 触摸 + 物理按键
平台 API 只能用浏览器 API 只能用 wx.*,登录、支付、转发、文件、扫码 走原生 SDK 桥接

这里最容易被忽略的是第三行:资源下载顺序不一样。Web 和原生都是按需加载,缺什么拉什么;微信小游戏环境里,包内资源不是按需加载,而是一次性加载所有包内资源,然后再启动页面。这个差异直接决定了主包不能塞太多东西。

还有一条硬规则:微信小游戏不能从远程服务器下载脚本文件。资源可以远程,代码不行。所以做包体优化时,能挪到 CDN 的只有资源,脚本体积得在工程侧想办法压。

4.2 同一份代码,哪些地方要写分支

我用三套判断,从粗到细。

第一层,sys 里的运行时属性。 sys.isNative、sys.isBrowser、sys.isMobile 是三个布尔量,sys.platform 是枚举。3.8 的 sys.Platform 枚举里,小游戏这一类包括 WECHAT_GAME、BYTEDANCE_MINI_GAME、BAIDU_MINI_GAME、ALIPAY_MINI_GAME、TAOBAO_MINI_GAME、XIAOMI_QUICK_GAME、OPPO_MINI_GAME、VIVO_MINI_GAME、HUAWEI_QUICK_GAME,原生是 ANDROID、IOS、WIN32、MACOS,Web 是 MOBILE_BROWSER、DESKTOP_BROWSER。

第二层,cc/env 里的编译期宏。 这些宏在构建时就会被常量替换掉,写在 if 里不产生运行时开销。

宏 含义
EDITOR / PREVIEW / BUILD / TEST 运行模式,四选一
DEV 编辑器、预览、测试模式下为真
DEBUG DEV 或调试模式构建时为真
HTML5 / NATIVE Web 端 / 原生端
WECHAT / BYTEDANCE / ALIPAY / BAIDU / VIVO / OPPO / HUAWEI / XIAOMI 各小游戏平台
MINIGAME 任一小游戏平台为真
RUNTIME_BASED OPPO、VIVO、HUAWEI、COCOSPLAY 等基于 Runtime 的快游戏
JSB 等价于 NATIVE
SUPPORT_JIT 小游戏与快游戏为假

注意 SUPPORT_JIT 这一行:小游戏和快游戏环境不做 JIT,代码全靠解释执行,所以源码写得啰嗦一点,别在热路径里堆复杂表达式。

第三层,具体平台的 SDK 只能运行时判断。 微信的登录、支付、转发这些 API 在 Web 上根本不存在,只能这样写:

import { sys } from 'cc';

export function platformTag(): string {
    if (sys.isNative) {
        return 'native';
    }
    if (sys.platform === sys.Platform.WECHAT_GAME) {
        return 'wechat';
    }
    return 'web';
}

Skyline Courier 里需要写分支的地方不多,就三处:存档存储(Web 用 sys.localStorage 转发到 window.localStorage,小游戏转发到 wx.setStorageSync 一类,原生转发到文件系统);分享与登录(只有微信端有);震动反馈(sys.isMobile 判断,小游戏需要额外调 wx.vibrateShort)。

条件编译也可以用在文件级别,用 // @ts-ignore 加 if (WECHAT) 包住整段。更稳妥的做法是把这个平台逻辑收进一个 PlatformService 接口,微信版和 Web 版各实现一份,业务代码只依赖接口。

4.3 Web 端:静态托管和 CDN 那点事

3.8 的 Web 平台有两个模板。Web Mobile 默认把游戏视图撑满整个浏览器窗口,Web Desktop 允许指定固定分辨率(默认 1280 × 960),之后视图不随窗口变化。Skyline Courier 是横屏小游戏,Web 端我一律用 Web Mobile。

Web 专属选项不多:资源服务器地址(remoteServerAddress)、WEBGPU、预览分辨率、Polyfills、vConsole(仅 Web Mobile)。

关于 Polyfills:Web Mobile 提供 async Functions 和 coreJs 两种,打包脚本时会做对应处理。这东西的作用是给老浏览器补新语法。我的判断是,如果你的目标用户里有 iOS 12 以下的老设备,coreJs 值得开;只面向近三年机型就别开,白白涨包体。

关于 资源服务器地址:不填的话,remote 文件夹会被打进游戏包里;填了的话不打包,构建完你得手动把 remote 整个传到那台服务器上。传的时候注意保持目录结构,config.json 里的路径是相对的。

部署本身没有玄学。构建完成后点文件夹图标,把对应任务目录里的东西整个拷到 Web 服务器根目录就行,Apache、Nginx、IIS 都行。官方文档明确提了两点:一是 WebGPU 构建需要 http-server 或 nginx 这类 HTTP 服务器来访问,不能直接双击 html 文件;二是 Retina 在构建到 Web 平台时默认开启,脚本里 view.enableRetina(true) 可以再显式设置一次。

浏览器兼容性,官方测试过的桌面浏览器是 Chrome、Firefox(火狐)和 QQ 浏览器,移动端是 Safari(iOS)、Chrome(Android)、QQ 浏览器(Android)和 UC 浏览器(Android)。顺带一句,别开 IE 兼容模式。

4.4 原生端:版本要求和那些硬门槛

原生构建的环境依赖比想象中挑。

环节 要求
JDK JDK 17(Android Studio 2022.2.1 / 2022.3.1 配套)
Android Studio 2022.2.1 或 2022.3.1(3.8.0 及以上引擎版本适用)
Android NDK 推荐 r21 ~ r23;Apple M 系列芯片推荐 r24
Android SDK Platform 主流 API Level 26(8.0)、28(9.0)等
Xcode 14 及以上(Mac 下构建 iOS 与 macOS)
Visual Studio 2019 / 2022 社区版,勾「使用 C++ 的桌面开发」和「使用 C++ 的游戏开发」

系统版本下限,官方「特性与系统版本」页给了一张表:VULKAN 渲染后端要求 Android API Level 24(7.0);延迟渲染管线要求 API Level 21(5.0)/ iOS 10.0;TaskFlow 任务调度系统要求 API Level 18(4.3)/ iOS 12.0,同时会自动开启 C++17。3.6.0 以上的 Creator 用 C++17,最低 API Level 21、iOS 11.0。

几个原生构建选项值得单独说:

渲染后端(Android)支持 VULKAN、GLES3、GLES2 三种,默认 GLES3。注意勾了 GLES2 就必须同时勾 GLES3,不允许单选。多勾的时候运行时按设备实际支持情况挑。

APP ABI 提供 armeabi-v7a、arm64-v8a、x86、x86_64 四种。只发国内渠道的话留 arm64-v8a 和 armeabi-v7a 就够,包体会小一半。

平滑帧率(Swappy)会主动配合屏幕刷新率调整渲染时间,达到稳定帧率、减少无效绘制。GLES 和 Vulkan 都支持。这项我建议开。

iOS 的「跳过 Xcode 工程的更新」只在用 CocoaPods 集成过第三方 SDK 之后才勾。默认情况下每次构建都会执行 CMake 命令重新生成 Xcode 工程,你的 CocoaPods 配置会被还原掉。

目标版本(iOS)默认是 12.0,构建后写进 proj/cfg.cmake 的 TARGET_IOS_VERSION。

原生构建完,产物在 build/<平台>/proj,用对应的 IDE 打开就能继续调试。编辑器里也能直接「生成」「运行」,但首次编译 Android 工程或版本升级后,官方明确要求先「用 Android Studio 打开工程,检查是否有缺失的工具和错误」,确认无误后再用面板按钮生成和运行。

还有两个坑:MIUI 10 上跑 debug 模式构建的工程,可能弹「Detected problems with API compatibility」,这是 MIUI 自身的问题,用 release 模式构建就行。另外 iOS 打 App Store 时,如果项目没用 WebView,去 项目 -> 项目设置 -> 功能裁剪 里把 WebView 模块剔除,官方说这么做能提高机审成功率。

4.5 微信小游戏相比 Web,额外少了什么

除了 4.1 表格里写的,官方文档里明确列了一条:微信小游戏不支持 WebView。想在小游戏里嵌个网页,这条路是堵死的。

还有几个平台 SDK 的移植工作得自己做:用户接口(登录、授权、用户信息)、微信支付、转发、文件上传下载、媒体(图片、录音、相机)、位置、设备信息、扫码、NFC。引擎层面已经把框架适配好了,但业务代码里的调用得你自己包一层。

关于 WebAssembly 也有版本门槛:微信 WebAssembly 要求微信版本 v7.0.17 及以上,微信开发者工具的调试基础库要求 v2.12.0 及以上。另外微信小游戏引擎插件目前仅支持 js 模式,所以如果你打算用「分离引擎」来省主包体积,Wasm 3D 物理系统就得关掉。这是个取舍点,我没在 Skyline Courier 里用分离引擎,因为我的物理是 2D 的 box2d,本来就不打 wasm。

顺带说一句微信高性能模式,它是 iOS 侧的,不是全平台。开启后性能提升明显,但有两条硬约束值得提前知道:iOS 下内存限制压到 1G(2G RAM 机型)到 1.5G(中高端机);iOS 下不支持 ETC 和 PVRTC 压缩纹理格式,只剩 ASTC 和 ETC1。也就是说你要做压缩纹理配置,得留一份 ASTC 的分支。

4.6 相关阅读