ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CocosCreator H5游戏启动页完全自定义:从换Logo到进度条与版本号

CocosCreator H5游戏启动页完全自定义:从换Logo到进度条与版本号 发布过 CocosCreator 的 H5 项目之后你大概率经历过这个场景构建完 web-mobile 包传到服务器自己在浏览器里一打开先是白屏然后屏幕中央慢慢浮出引擎默认的启动动画和那个闪亮的 Cocos 标志转几圈之后才进入游戏。这时候产品经理凑过来说启动页能不能换成我们公司的 Logo这个引擎默认动画太不像自己人了。我最初的想法很简单打开编辑器项目设置换张图、换个背景色就完了。真动手才发现编辑器内置的启动页配置只能改改图片、背景色和动画样式想加个版本号、换进度条、展示一段品牌文案或者让它和公司官网风格统一全都做不了。H5 平台的启动页又比原生平台特殊没有原生闪屏那种系统级机制本质上就是浏览器里的一段 HTML 和 CSS。这篇文章我就把自己从换图到完全自定义启动页的完整过程写出来包含 2.4.x 和 3.x 两个大版本的处理方式以及嵌入微信公众号、封装 App WebView 壳包时的适配经验希望能帮你少走弯路。1. 启动页的本质浏览器、引擎Loader和游戏场景三者交接的过程想自定义启动页先得搞明白 H5 游戏从打开网页到进入游戏中间到底发生了什么。这一步理解不透后面做定制很容易出现自定义启动页显示了但引擎启动后又把它盖住了或者游戏画面都出来了启动页还没消失这种尴尬问题。1.1 H5 没有原生闪屏启动页本质是一段网页原生 App 的启动页是系统层面控制的App 进程启动时系统先显示一张静态图等应用第一帧渲染出来再切换。H5 完全不是这个逻辑用户在浏览器里输入 URL、点回车浏览器下载的是 HTML 文件然后在页面里执行 JavaScript拉起 Cocos 引擎引擎再加载游戏资源最后 canvas 渲染出游戏画面。这中间有个关键的客观限制引擎代码本身也是 JS 脚本需要下载和执行游戏资源更是几百 KB 到几十 MB 不等需要通过网络加载。在引擎把资源准备妥当之前游戏画面是画不出来的canvas 只会是一片空白。启动页的作用就是在这段引擎还没准备好的空窗期里用最传统的网页技术——HTML、CSS、简单 JS——展示一个有内容的画面告诉用户页面加载中别走。理解了这一点就明白了一个关键结论自定义启动页的核心不是去改引擎渲染逻辑而是在 index.html 里塞一段独立的、不依赖引擎资源和 API 的网页层然后在整个引擎加载过程中始终让它保持在最上层直到游戏主场景真正跑起来再把它移除。1.2 从 index.html 到第一帧画面中间有三个阶段我把整个加载过程拆成三个阶段后面所有方案都会围绕这三个阶段的时序来设计第一个阶段是 HTML 解析与样式加载。浏览器拿到 index.html 后从上往下解析构建 DOM应用 CSS。这时候引擎还没跑起来。所以自定义启动页的 HTML 和 CSS 必须放进 index.html 的 body 最前面且尽量内联减少额外请求。第二个阶段是引擎启动与资源加载。浏览器执行 main.js拉起引擎初始化流程引擎开始按照 settings 里记录的包体和资源清单去加载 JS 脚本、图片、音频、json 等文件。这个阶段耗时最长也是启动页存活的主战场。引擎自己的默认启动页就是在这个阶段显示的。第三个阶段是主场景启动。所有必备资源加载完成引擎初始化完毕开始启动第一个游戏场景。通常在场景根节点挂载的脚本触发 start 回调时游戏画面已经能正常渲染了这时候就是移除自定义启动页的最佳时机。这三个阶段里最后这个移除时机是最容易做错的。有人习惯在引擎初始化完成事件里移除启动页结果资源加载还在继续canvas 还是白屏启动页一消失页面又变回一片空白。正确做法是把移除动作放到主场景启动之后。2. 编辑器内置启动页设置的边界什么项目够用什么项目必须动手先说结论如果你的项目只需要换张 Logo、换个背景色加载动画选一种还算顺眼的样式那么编辑器自带设置完全够用五分钟就能搞定没必要折腾代码。但如果你希望启动页上有公司全称、版本号、自定义进度条、品牌 Slogan或者整体视觉风格和官网、公众号保持一致那内置设置就无能为力了必须走自定义方案。2.1 内置设置的入口与能调的东西CocosCreator 2.4.x 里入口在项目菜单 - 项目设置 - 项目数据 - 启动图相关配置3.x 版本在项目菜单 - 项目设置 - 项目数据 - 启动画面不同小版本名称略有差别有的叫启动图有的叫Splash Screen。在这里可以设置背景颜色启动页的底色。这个自由度还比较大可以用品牌色但注意引擎默认的加载动画颜色也会受底色调和影响深色背景下引擎 Logo 和加载动画的观感需要实际构建验证。启动图 Logo 图片替换默认的 Cocos 标志。推荐使用带透明通道的 PNG尺寸控制在 512x512 以内文件体积尽量小于 200KB。图片太大会拖慢启动页本身的显示速度反而增加白屏时间。动画样式2.4.x 有几套内置动画3.x 提供了淡入淡出、缩放、旋转等选项。真正可调的参数不多基本是单选加一个时间参数。3.x 额外提供点击跳过启动图选项打开之后用户可以点一下自动跳过。这个功能在真机上有用但如果时机没卡好用户点了跳过时资源还没加载完容易直接露出白底 canvas视觉上比不跳过还糟糕。2.2 内置方案的一个隐藏坑iOS 上黑底闪烁内置设置的背景色如果选了深色在部分 iOS 设备的浏览器里页面加载初期会出现一段极短暂的黑底闪烁然后才变成你设置的颜色。原因是浏览器先渲染了 body 默认的白色背景再应用引擎模板里设置的背景色最终才是项目设置里的启动图背景色这个过程是分步的肉眼在性能差的设备上能感知到。这个坑就是内置方案不好解决的典型例子因为你没办法控制模板里 body 的初始背景色。而用自定义方案直接在 index.html 的内联 CSS 里把 body 背景色写死从浏览器解析 HTML 的第一毫秒就是品牌色彻底避免闪烁。所以我的判断标准是这样的个人开发者做测试包、内部体验包或者产品上线时间紧、品牌要求不高用内置设置就行别浪费时间。但只要是商业运营项目、需要对外露出品牌形象的项目哪怕只是加一个版本号也建议直接走入自定义方案。反正改一次能复用所有项目一劳永逸。3. 三条自定义路线构建模板覆盖、main.js接管、改引擎源码怎么选自定义启动页的方案社区里来来去去就那么三条路线。我全都试过每种方案的优缺点和适合场景不太一样这里详细拆开讲。3.1 路线一build-templates 构建模板覆盖最推荐CocosCreator 提供了一个构建模板机制在项目根目录下创建和构建平台同名的 build-templates 目录构建时会用这个目录里的文件覆盖引擎默认生成的对应文件。具体操作是在项目根目录建build-templates/web-mobile/然后把你想要定制的 index.html 放进去。问题来了——默认的 index.html 长什么样最简单的方式是先用默认配置构建一次打开构建产物build/web-mobile/index.html把它复制到这个目录里然后在复制出来的文件上做修改。这样你始终是基于当前引擎版本的真实模板在改不会因为版本差异导致覆盖后页面错乱。在模板里你可以随意调整 head 和 body 结构、引入自定义 CSS 和 JS、增加自定义 DOM。构建时引擎会把整个目录里的文件原封不动地复制到 web-mobile 目录替换同名文件。这个方案的最大优势是改动范围集中在 HTML 层不碰引擎加载逻辑引擎升级时只要重新生成一份模板、把改动的部分迁过去就行。我的所有项目基本都是用它这也是给团队做技术方案评审时的首选。3.2 路线二main.js 接管加载流程控制力最强但兼容成本高如果你不仅要做视觉自定义还想彻底控制加载流程——比如不用引擎默认的加载方式、自己做分包加载策略、或者想在启动页上展示真实的资源加载进度——那就需要动 main.js。2.4.x 的 main.js 是一个普通脚本你可以直接在里面找到引擎初始化相关调用在cc.game.run之前插入自己的逻辑把默认 splash 相关代码注释掉。但 3.x 的构建产物变化很大main.js 变成模块入口真正的启动流程在 application.js 里用 SystemJS 方式加载直接改 main.js 的复杂度明显上升。我的建议是不到万不得已不要直接改 main.js 去对抗引擎默认加载流程。构建产物每次构建都会重新生成改动无法固化必须配合 build-templates 一起用。而且 CocosCreator 不同小版本之间的 main.js 结构差异很大你这次适配好了引擎升个小版本可能就不兼容。3.3 路线三修改引擎源码不推荐但必须知道还有一部分团队会选择直接改引擎包里的 SplashScreen 相关源码然后通过自定义引擎插件把修改固化下来。这样做的好处是理论上能做任何深度定制比如改默认动画的运动曲线、增加粒子特效、甚至把启动页做成一个 3D 场景。缺点是极其明显引擎源码改动无法跟随引擎升级每次升级都要重新做补丁维护成本很高。除非你们团队有专门的引擎层维护人员否则我不建议个人开发者或者小团队碰这条路。多数情况下build-templates 加一个 UI 层启动页已经足够满足业务需求了。3.4 方案对比一览方案灵活性实现成本引擎升级兼容性适用场景内置启动页设置低极低好快速测试、低品牌要求build-templates 覆盖高中较好大多数商业项目首选main.js 接管流程很高高较差需适配需要深度控制加载流程修改引擎源码极高极高差团队有引擎定制能力4. 实战一个可复用的品牌启动页Logo 进度条 版本号下面进入核心实操。我以 2.4.x 和 3.x 都能通用的方式实现一个包含品牌 Logo、模拟进度条、版本号的自定义启动页并在主场景启动后自动消失。这个方案我在实际项目里验证过多次稳定可靠。4.1 页面结构与样式内联CSS不依赖任何额外请求第一步在项目根目录下创建build-templates/web-mobile/目录。然后构建一次默认项目把build/web-mobile/index.html复制到这个目录里开始改动。在 index.html 的 body 最前面插入以下结构div idcustom-splash div classsplash-middle img classsplash-logo src./splash-logo.png altlogo / div classsplash-progress div classsplash-progress-inner/div /div div classsplash-versionv1.0.0/div /div /div对应的内联 CSS 放在 head 标签里使用内联样式而不是外部 CSS 文件是因为要确保在浏览器解析到这一行时立刻有样式不需要等待额外的 CSS 请求返回减少白屏时间。style html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: #1a1a2e; } #custom-splash { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 9999; background: #1a1a2e; display: flex; align-items: center; justify-content: center; } .splash-middle { text-align: center; } .splash-logo { width: 160px; height: 160px; object-fit: contain; } .splash-progress { width: 200px; height: 4px; background: rgba(255,255,255,0.2); border-radius: 2px; margin: 24px auto 12px; overflow: hidden; } .splash-progress-inner { width: 0%; height: 100%; background: #e94560; border-radius: 2px; transition: width 0.1s ease-out; } .splash-version { color: rgba(255,255,255,0.6); font-size: 12px; font-family: sans-serif; } #custom-splash.splash-hide { opacity: 0; transition: opacity 0.3s ease; pointer-events: none; } /style这里有几个细节值得说明。position: fixed加z-index: 9999是为了让它始终悬浮在引擎 canvas 之上不管引擎初始化时 canvas 的 z-index 是多少都不容易覆盖掉它。背景色直接写死在 html 和 body 上解决前面提到的 iOS 黑底闪烁问题。Logo 尺寸不写死固定值而是用宽度 160px是为了适配不同分辨率的屏幕低端机上图片像素不必用原始大图。注意splash-logo.png这个文件要放在build-templates/web-mobile/目录下构建时会一并复制到输出目录和 index.html 保持相对路径引用。4.2 模拟进度条平滑比准确重要很多教程会教你从引擎那边拿真实加载进度。2.4.x 时代cc.loader.onProgress还能拿到但 3.x 之后资源加载逻辑变了拿真实进度的成本明显变高而且拿到的数值也未必是平滑的。试试就明白H5 游戏资源并不是一次性全下载完的主场景先用到的会优先加载后面场景的资源可能延迟加载如果展示真实进度进度条会走几格卡住不动然后突然跳到满观感非常差。所以我用的是伪进度方案启动页启动后进度条自己匀速往前跑前 3 秒内走到 85% 左右然后减速等待。等主场景真正启动、准备移除启动页时先把进度条强制拉到 100%再淡出。用户看到的永远是平滑前进的进度条心理感受比准确但卡顿好得多。在 index.html 底部加一段独立 JSscript (function () { var inner document.querySelector(.splash-progress-inner); var progress 0; var timer null; function update() { progress Math.random() * 8 2; if (progress 85) { progress 85; clearTimeout(timer); timer setTimeout(update, 300); return; } inner.style.width progress %; timer setTimeout(update, 120); } update(); window.finishCustomSplash function () { clearTimeout(timer); inner.style.width 100%; setTimeout(function () { var splash document.getElementById(custom-splash); if (splash) { splash.classList.add(splash-hide); setTimeout(function () { if (splash splash.parentNode) { splash.parentNode.removeChild(splash); } }, 350); } }, 150); }; })(); /script这个函数逻辑不复杂update按随机步长推进进度条到 85% 后降速全局提供finishCustomSplash方法游戏侧在合适的时机调用进度满格后淡出并移除整个 DOM。用 setTimeout 而不是 setInterval是为了让每次推进的间隔可控避免在低端机上因为主线程被引擎加载占用导致定时器堆积。4.3 游戏侧代码在主场景真正渲染出来后才关闭启动页自定义启动页的 DOM 已经就位了接下来需要在游戏主场景挂一个脚本负责在合适的时机调用finishCustomSplash。新建一个 TypeScript 组件脚本import { _decorator, Component, Node } from cc; const { ccclass } _decorator; ccclass(SplashClose) export class SplashClose extends Component { start() { // 主场景已经启动首帧渲染已在眼前 if (window[finishCustomSplash]) { window[finishCustomSplash](); } } }把这个组件挂载到主场景的 Canvas 根节点上主场景一激活、start 被调用启动页就开始淡出。有些项目会担心 start 触发的时机是否保证首帧已渲染说实话大部分情况下是够的因为 start 时序在场景首次更新之前但 DOM 的淡出动画有 350ms覆盖了首帧渲染的间隙实际体验不会出现白屏闪烁。如果你对时机有更高要求可以监听director的事件在稍晚一点的时机执行。例如 2.x 里用cc.director.on(cc.Director.EVENT_AFTER_SCENE_LAUNCH, ...)。但实测下来普通项目用组件 start 方案就够了没必要多绕一层。4.4 关闭内置启动页避免双重启动画面这是容易被忽略的一步。不关掉引擎内置启动页的话你会看到自己的品牌页显示了一会儿然后突然切到引擎的启动动画再闪进游戏非常不协调。关闭方式分版本2.4.x 在项目设置里没有直接关闭入口但可以在 build-templates 覆盖的 index.html 后面补一段隐藏逻辑等引擎初始化后把引擎创建的启动面板隐藏掉。引擎默认的 splash DOM 节点有一个固定 ID在 2.4.x 里通常是#SplashPanel。可以加一段判断在检测到它出现时把它 display 设为 none。3.x 版本在项目设置 - 项目数据 - 启动画面里有一个 显示启动图 开关或者类似的选项把它关掉即可构建产物里就不会再生成引擎默认动画。如果你的 3.x 版本没有这个开关就用 2.4.x 那种兜底方式检测到对应 DOM 就隐藏。一个经验之谈关闭内置启动页之后引擎初始化到 canvas 出现之间可能出现极短的白屏这正好是自定义启动页露脸的时间所以自定义启动页的 CSS 一定要内联且在最前面任何额外的请求延迟都会让这段白屏暴露出来。4.5 构建、验证与检查清单构建方式照常选择 web-mobile 平台构建。构建完成后产物目录的 index.html 应该包含你插入的自定义启动页代码。本地验证建议用静态服务器不要直接双击 index.html 用 file 协议打开Cocos 引擎加载 JSON 和脚本时对 file 协议的兼容性很差很容易报跨域错误。验证时打开浏览器开发者工具切到 Network 面板把网络节流从无限制切到 Slow 3G刷新页面重点检查这几项页面打开的第一时间是否直接显示品牌底色有没有白屏闪烁Logo 是否正常加载尺寸和位置是否合理进度条是否平滑前进有没有卡顿从启动页淡出到游戏画面出现之间有没有白屏间隔低端机或性能降速后启动页 DOM 是否始终在 canvas 之上这个检查清单我每次发版前都会过一遍几乎每次都能发现一两个问题强烈建议你也养成习惯。5. 2.4.x 与 3.x 的启动页差异迁移时最容易踩的坑如果你是从 2.4.x 老项目升级到 3.x或者团队里两套版本并行启动页这块有几个坑值得提前知道。5.1 构建模板的结构变化2.4.x 的 web-mobile 构建产物比较规整jsc 脚本、配置、资源分层清晰。3.x 的构建产物引入了更多模块化机制构建模板里除了 index.html还多了一堆由 SystemJS 动态加载的脚本和资源。表面上看着复杂但 build-templates 的覆盖机制是通用的你依然只需要覆盖 index.html 就够了其他文件不动。真正要小心的不是模板本身而是你往 index.html 里插自定义代码时别破坏 3.x 版本脚本的加载顺序。3.x 的 index.html 里通常有一个固定顺序的脚本加载段在 head 底部或 body 里有引入 main.js 的代码。你的自定义页面结构插在 body 最前面没问题但不要随意删除或移动引擎的脚本引用。5.2 main.js 的差异与关闭内置启动页方式不同2.4.x 关闭内置启动页可以通过直接在 main.js 里注释掉相关代码实现因为结构相对简单。3.x 的 main.js 变成了入户籍口真正的初始化逻辑在 application.js 里直接改的代价很大。所以在 3.x 里优先用编辑器设置关闭其次是 build-templates 里做 DOM 隐藏不碰 main.js。我迁移项目时就在这里吃过亏把 2.4.x 的一套 main.js 修改直接平移到 3.x 构建模板里结果页面直接白屏。排查了半天才发现3.x 的 main.js 是 ESModule 形式在模块加载完成之前你插在文件里的自定义同步代码根本不会执行而模块异步加载的顺序又不受你控制。从那以后我在 3.x 里基本只做两件事编辑器设置关闭内置启动页build-templates 里盖自己的 HTML 层没再出过兼容性问题。5.3 设置项绑定的全局变量变化2.4.x 里启动图背景色和图片配置会写进 settings3.x 里也有类似设置但字段名、位置都变了。如果你做了一套供多个版本项目共用的 build-templates注意去读对应版本的设置字段。我的习惯是为 2.x 和 3.x 各维护一套模板目录互不混用。因为两边产物结构差异太大强行复用一套模板只会增加排查成本没有任何收益。6. 公众号内嵌与WebView壳包场景下的启动页调优前面说的都是纯 H5 发布场景。实际运营中很多 CocosCreator H5 游戏的最终形态并不是一个独立网页而是嵌入微信公众号或者用封装工具打成一个 App 壳包让用户通过 App 打开游戏。这两个场景对启动页有一些额外要求。6.1 微信公众号内打开启动页要短、要轻、不能挡授权微信内置浏览器对首屏性能非常敏感用户在朋友圈或公众号文章里点开一个链接等待超过三秒就会流失大半。启动页如果做得太重——大背景图加字体文件加动画——会显著拉长首屏交互时间。我在适配公众号场景时的经验是Logo 图片压缩到位尽量用 WebP 格式体积控制在 50KB 以内不再用大的 PNG。进度条动画全部用 CSS transition不要用 JS 做逐帧动画。微信浏览器的 JS 线程和渲染线程竞争激烈JS 动画的掉帧概率比普通浏览器高很多。启动页中不要做任何需要用户授权的操作比如定位弹窗、微信 JSSDK config 注册都放到启动页消失、进入游戏场景后再处理。否则用户看到一个启动页又弹一个授权框观感像网页出问题了一样。6.2 App 内嵌 WebView 壳包两层启动页要衔接好现在有不少工具支持把 H5 打包成 Android 或 iOS 壳包壳包本质是一个全屏 WebView加载远程或本地打包的 H5 资源。这种情况下页面会出现两层启动页壳包自带的原生启动图以及 H5 内部的启动页。常见的糟糕体验是用户打开 App先看到原生启动图然后 WebView 开始加载白屏一段时间设计好的 H5 启动页才显示出来。中间这段白屏是壳包加载 H5 资源产生的自定义启动页没法完全消除但可以缓解。做法是在壳包的原生启动图上做文章让原生启动图尽量做成和 H5 启动页视觉接近的样子颜色、图案一致切换的时候用户几乎感知不到变化。另外注意 WebView 的缓存策略。壳包首次加载时H5 所有资源要通过网络拉启动页出现得晚是正常的后续打开如果壳包启用了缓存启动页会快很多。建议壳包侧做好 WebView 缓存配置否则每次打开都像第一次加载启动页体验极差。6.3 顺带提醒H5 前端代码完全可见因为 H5 的 JavaScript 和 HTML 都是明文传输到浏览器端的用户按 F12 打开开发者工具所有源码一目了然启动页代码也不例外。不要在启动页相关逻辑里放任何密钥、内部接口地址、敏感的业务判断。启动页的显示和隐藏时序也不要用作任何安全校验的依据。之前遇到过有人把启动页加载完成后是否走完某段逻辑当成一种客户端验证手段结果被别人直接把 JS 里的判断结果改了轻松绕过。这类逻辑放在纯前端本身就是不安全的纯属给自己挖坑。7. 最后分享两个实战小技巧第一个是模拟低端机环境。真机低端机和 PC 浏览器的表现差异非常大尤其是启动页动画和引擎初始化并发跑的时候。你可以在 Chrome 开发者工具的性能面板里开启 CPU 降速选择 6 倍速或 20 倍速再配合网络节流基本能模拟出百元安卓机的体感。用这个方式测试启动页覆盖效果和进度条流畅度比等真机反馈高效得多。第二个是版本号的自动注入。手动改 index.html 里的版本号很容易忘我后来改成在构建脚本里做字符串替换从 package.json 里读取版本号替换掉模板里的占位符{VERSION}这样每次发版版本号保证一致。这个思路不限于启动页整个网页层面的信息展示都可以用占位符方案自动化程度越高出低级错误的概率越低。自定义启动页看起来是个小需求做深了之后涉及的时序、兼容性、性能问题其实不少。希望这篇基于实际踩坑经验的分享能用得上你的启动页开发过程可以顺利一些。
返回列表