180 lines
17 KiB
Markdown
180 lines
17 KiB
Markdown
# 方块图片远程加载
|
||
|
||
新增的 `BlockBtnUI.png`、`door.png`、`down.png` 必须与同名 plist 成对上传,共用同一个版本号;其布局来自远程 plist,说明见 [公共图集远程加载](gameplay-atlas-loading.md)。下文仅上传 PNG 的说明适用于普通方块。当前代码只准备当前关和三张公共图集;下文关于两关窗口的描述属于旧方案。
|
||
|
||
## 当前方案:原始 PNG + 1.0.0 版本号
|
||
|
||
Block 图片直接使用源 PNG,不加载 Cocos Asset Bundle,不需要 config.<Hash>.json、import、native 等构建产物。微信/抖音小游戏的新下载直接写入 `USER_DATA_PATH/Block/<version>/`,不进入 Cocos 持久缓存队列;assets/Block 当前保持非 Bundle 配置,音频及其他游戏资源的缓存设置不受影响。当前仍按单张 PNG 下载,不使用 ZIP。
|
||
|
||
配置在 GameConfig.ts:
|
||
|
||
- BLOCK_IMAGE_BASE_URL:默认 https://cdn.pay.nika4games.com/remote/Block/。
|
||
- BLOCK_IMAGE_VERSION:默认 1.0.0,只在没有可用的远程版本和同地址历史版本时兜底。
|
||
- 每次冷启动从上述目录读取 version.json,不依赖客户端的构建 Hash。
|
||
|
||
已添加 assets/Block/version.json:
|
||
|
||
```json
|
||
{
|
||
"version": "1.0.0"
|
||
}
|
||
```
|
||
|
||
版本格式为三段数字,例如 1.0.0、1.0.1、2.0.0。按字符串是否一致决定缓存命名空间,不按大小阻止回退;不要把同一个版本号重复用于不同图片内容。
|
||
|
||
## 上传与后续换图
|
||
|
||
首次发布包含这套逻辑的客户端后,将 assets/Block 中的 **PNG 和 version.json** 原样上传到 CDN 的 remote/Block/。无需上传 .meta,不需要为图片制作 Cocos 构建包或 ZIP。
|
||
|
||
以后换图:
|
||
|
||
1. 替换远程对应 PNG,保留文件名,保持画布尺寸及透明边距兼容。
|
||
2. 确认新 PNG 可访问,处理 CDN 缓存。
|
||
3. **最后修改远程 version.json**,例如将 1.0.0 改成 1.0.1。只替换 PNG 而不改号,已缓存的玩家会继续使用旧图。
|
||
4. 重新启动游戏,检查图片与本地缓存记录。远程文件修改由资源维护方完成,本次代码没有上传 CDN。
|
||
|
||
图片请求示例:
|
||
|
||
```text
|
||
https://cdn.pay.nika4games.com/remote/Block/1color0.png?v=1.0.1
|
||
```
|
||
|
||
**CDN 要求**:PNG 缓存键需包含 v 参数,或者换图时刷新对应 PNG 缓存;version.json 配置 Cache-Control: no-store 或等效的不缓存规则。客户端版本请求带时间戳、不进入 Cocos 文件缓存,但不能代替 CDN 配置。小游戏域名需具备 request / downloadFile 权限;浏览器预览还需要正确的跨域配置。
|
||
|
||
这是整套图片的一个总版本号。即使只改一张图,改号后也会逐步重新缓存整套图片,不再像按文件 MD5 时那样复用未修改文件。前台仍只等待当前关及下一关,不等待全量下载。
|
||
|
||
同一路径覆盖 PNG 不会在服务器保存历史内容。当前会话固定使用启动时选择的 v 参数,但旧会话若缺少某张图片,覆盖后仍可能下载到新内容;若要严格保存各历史版本,需要服务器按版本目录保留图片,本方案不包含该部署方式。
|
||
|
||
## 游戏内行为
|
||
|
||
1. GameManager.onLoad 启动独立单例 BlockAssetManager 的后台任务。场景切换不会销毁任务。
|
||
2. 先确定本次版本;后台下载与进关等待共用同一个检查。清单最多等待 5 秒,缺失、损坏或网络失败时使用同一 CDN 地址上次记录的有效版本,否则使用 GameConfig 的默认版本。
|
||
3. 切场景、切关、回到前台不重新选择版本;下次冷启动才检查后续更新。回到前台仍会检查当前版本的磁盘文件是否完整。
|
||
4. 小游戏后台使用平台 `downloadFile` 将 PNG 直接写入版本目录,最多 8 路滚动下载,不解析全量图片,不创建全量 Texture2D / SpriteFrame。前台加载期间停止派发新的后台请求;进关图片就绪后自动继续,不等玩家通关,切场景不取消任务。
|
||
5. 当前关和实际下一关的图片就绪后才能进入游戏。包含叠层、变色、问号和开关状态;读取下一关不改当前数据或玩家进度。无尽模式预选下一关并在通关时复用。
|
||
6. **仅当前关或下一关的图片需要远程下载时**显示 Loading:HomeScene 调用 JiaZai.openLoad/closeLoad,GameScene 调用 SceneManager.openLoad/closeLoad。内存复用、有效本地/临时文件、单独读取关卡 JSON 或版本清单均不打开 Loading;缓存文件仍需加载到内存、图片就绪后才进入游戏。缓存损坏重下、等待后台正在下载的所需图片也会打开 Loading。图片就绪后关闭,失败则关闭并显示重试。后台单独运行不显示 Loading;新版本失败不使用旧图冒充成功。
|
||
7. 旧场景显示期间保留它的图片;销毁后释放旧关独占的动态 SpriteFrame、Texture2D 引用和图片解析缓存,共享纹理保留。不会清空磁盘或音频缓存。
|
||
8. 浏览器预览同样按原始 PNG 地址加载,需要先上传图片或将 BASE_URL 指向本地静态图片服务;浏览器不进行全套后台磁盘预缓存。
|
||
|
||
## 下载并发与进关优先
|
||
|
||
后台使用 **8 个下载 worker**:一张完成就领取下一张,不等待整批 8 张全部结束;每张结束后只让出一次事件循环,没有 50 ms 或 500 ms 的逐图等待。仅下载当前版本目录中缺失的文件。
|
||
|
||
BlockAssetManager 自己维护下载队列:后台优先级为 `-10`,总下载上限为 10;进关请求优先级为 `10`。即便 8 个后台请求都未结束,进关仍可额外开始 2 个请求。排队中的同名后台任务被进关命中时会提升为前台任务,已经开始的同名任务由前后台共用同一个 Promise,不会重复下载。没有修改 Cocos 下载器的全局并发,也不会改变音频等资源的缓存参数;平台和网络仍可能限制实际并发。
|
||
|
||
准备当前关、下一关图片期间,后台 worker 不再领取新文件;已经发出的请求继续完成。图片准备完成或失败后,worker 自动恢复(等待期间每 100 ms 检查一次),进入 GameScene 后继续补齐其余文件,不需要再次调用启动。暂停只针对进关图片准备,不会暂停整局游戏期间的缓存。
|
||
|
||
平台下载先写入目标文件旁的 `.tmp`,校验文件非空后同步重命名为最终 `.png`。最终文件一旦存在即算该张落盘完成,不再调用 Cocos `cacheFile` / `writeCacheFile`,因此 Block 图片不受 Cocos 默认 500 ms 持久缓存间隔影响。升级前遗留的有效 Cocos 持久文件仍可直接读取;遗留临时文件会优先复制到用户版本目录,避免重复下载。
|
||
|
||
## 后台重试与本地复查
|
||
|
||
后台只有一个任务和一个待执行定时器:
|
||
|
||
| 检查结果 | 后续处理 |
|
||
| --- | --- |
|
||
| 用户版本目录缺少文件,或下载/写入失败 | 本轮结束后 **10 秒**重试;已经直存的文件逐张跳过 |
|
||
| 存在升级前的有效 Cocos 临时文件 | 优先复制到用户版本目录;复制失败则 10 秒后重试,不重复下载该临时文件 |
|
||
| 所有最终 PNG 都存在且非空 | 写入 `complete:true` 并停止定时重试;冷启动或回到前台仍会校验实际文件 |
|
||
|
||
后台单张下载不叠加 Cocos 内部重试,失败交给上述 10 秒流程。前台进关仍立即请求所需图片,不等待后台定时器;如果命中正在等待 10 秒重试的缺图,会马上下载。回到前台也会主动检查,版本清单在同一次启动中不因这些重试重复请求。
|
||
|
||
## 图片范围与布局
|
||
|
||
当前目录实查为 **368 张 PNG**:原有 322 张方块/问号/开关图片,另有 46 张 ice_ / xz_ 图片。后台文件清单覆盖当前这些文件;本次没有改变新增 ice_ / xz_ 的显示接入,具体进入内存的图片仍由现有关卡收集逻辑决定。
|
||
|
||
BlockTexturePaths.ts 维护文件名规则,并保留现有 PNG 的裁剪矩形及偏移,避免改用原图后改变现有 Sprite 的大小和位置。更换图片需保持现有画布、透明边距和方块形状兼容;改变布局或增加文件名/形状时需要相应更新客户端配置,而非只改版本号。
|
||
|
||
预制体的方块 icon.spriteFrame 保持空引用。动态纹理设置 packable=false,以便独立释放。不要把全套图片重新绑定到场景或预制体。
|
||
|
||
## 本地缓存
|
||
|
||
小游戏最终文件位于 `wx.env.USER_DATA_PATH/Block/<version>/<name>.png`;抖音使用对应的 `tt.env.USER_DATA_PATH`。版本目录直接隔离不同图片版本,当前实现不会在新版本尚未完整下载时删除旧版本目录,也不会清理音频或其他 Cocos 缓存。
|
||
|
||
前台进关与后台扫描均检查实际文件:目录存在就不再创建,清缓存或换设备后目录缺失会重新创建。创建时使用平台递归目录参数;并发创建导致的“已存在”会复查目录后视为成功,权限错误或同名文件占位仍会抛出,不删除占位文件。
|
||
|
||
cc.sys.localStorage 的 block_image_cache_v1 示例:
|
||
|
||
```json
|
||
{
|
||
"version": "1.0.0",
|
||
"baseUrl": "https://cdn.pay.nika4games.com/remote/Block/",
|
||
"total": 368,
|
||
"cached": 368,
|
||
"complete": true
|
||
}
|
||
```
|
||
|
||
version 表示本次使用/缓存的目标版本;版本或地址改变先写 complete:false。只有版本目录下所有图片文件都存在且非空才写 true;`.tmp`、下载中断或磁盘不足都不等于缓存完成。这个记录是提示和诊断数据,不是跳过实际文件校验的依据。
|
||
|
||
旧的 Hash 版本记录不作为语义版本使用。标记不代替文件检查;换设备、缓存被清理、标记迁移但图片不存在时仍会补齐文件。图片解析失败会删除该 URL 的坏缓存并重试。
|
||
|
||
## 图片来源与下载日志
|
||
|
||
控制台过滤 `Block图片`。每次进关(包括重玩同一关)都会打印当前关、下一关、版本,以及各图片的实际加载来源。地图初始化的补充检查使用 `地图初始化` 标签,与切场景前的 `进关` 区分。
|
||
|
||
Cocos 微信非调试构建的启动日志级别为 `ERROR`,此时 `cc.log` 被引擎设置为空函数。Block 诊断统一使用 `console.log`,不修改游戏全局日志级别。修改 TypeScript 源码后需重新从 Cocos 构建微信包,再在开发者工具运行;只在开发者工具点编译不会重新构建 Cocos 源码。
|
||
|
||
后台任务也会打印完整状态,不能只根据是否出现单张下载日志判断是否执行:
|
||
|
||
- `后台缓存 / 启动`:已调用后台任务,随后检查版本与文件。
|
||
- `后台缓存 / 跳过`:输出平台下载接口、用户目录文件系统是否可用。
|
||
- `版本检查`:请求开始、成功版本,或失败时采用的回退版本。
|
||
- `缓存校验`:当前版本用户目录、有效最终文件数/总数、旧临时文件数、缺失数和完整状态。
|
||
- `全部本地缓存有效,无需下载`:已检查实际文件,当前不需要新增网络下载。
|
||
- `滚动下载队列,最多8张并发` / `在途=N/8`:后台队列的并发上限、已提交且尚未结束的请求数;实际网络并发还受平台及网络调度影响。
|
||
- `暂停派发,优先等待进关图片` / `继续后台缓存`:让前台先完成,已发出的请求继续;进关后自动恢复,不代表后台任务被场景销毁。
|
||
- `本轮结束`:全部已持久缓存,或说明缺失/旧临时文件数量并在 10 秒后重试。
|
||
|
||
本地存储的 `complete` 标记不能单独证明设备当前文件仍然存在;以本轮实际文件校验日志为准。日志文件数检查不等同于逐张解码图片完整性,损坏图片在前台解析失败时重试。
|
||
|
||
日志格式示例(文件名仅作示意):
|
||
|
||
```text
|
||
[Block图片][进关检查] 当前关50 / 下一关51,版本=1.0.0,共3张
|
||
[Block图片][进关来源] 当前关50 1color0.png -> 内存复用,版本=1.0.0
|
||
[Block图片][进关来源] 当前关50 2color1.png -> 本地缓存,版本=1.0.0
|
||
[Block图片][进关来源] 下一关51 3color2.png -> 远程下载,版本=1.0.0
|
||
[Block图片][远程加载] 开始 3color2.png,进关,https://.../3color2.png?v=1.0.0
|
||
[Block图片][远程加载] 成功 3color2.png,已直存并加载,https://.../3color2.png?v=1.0.0
|
||
```
|
||
|
||
- `内存复用`:SpriteFrame/纹理已经在内存。
|
||
- `本地缓存`:当前版本的持久缓存文件存在且非空。
|
||
- `临时缓存`:升级前 Cocos 留下的下载文件可复用,但不代表用户版本目录已经完整,同样不弹 Loading。
|
||
- `远程下载`:未命中以上缓存;开始请求或等待已有请求。正在下载的后台任务会标为 `等待后台下载`。
|
||
- `远程加载` 记录进关请求的开始、成功、失败;`远程下载` 记录后台文件请求的开始、成功、失败,均包含文件名和带版本号的 URL。
|
||
- 坏缓存会先记录缓存来源,再打印 `加载重试` 和远程来源。下载成功不代表 `complete:true`,全量缓存落盘状态以校验后的本地记录为准。
|
||
|
||
浏览器没有小游戏文件缓存接口,日志中的远程表示发起图片 URL 请求;实际是否由浏览器 HTTP 缓存返回,需在浏览器 Network 面板查看,不能仅凭此日志认定消耗了网络流量。
|
||
|
||
## 多玩家下载与 CDN 压力
|
||
|
||
本次目录统计:368 张 PNG,总大小 2,761,430 字节(约 2.76 MB / 2.63 MiB)。假设 10,000 名玩家都没有这个版本的本地缓存、且各自完整下载一次,约产生 **368 万次图片请求、27.6 GB 纯图片数据**;这不是实测并发压力,未计入协议开销、重试、版本 JSON 和其他游戏资源。
|
||
|
||
影响峰值的主要是同时首次下载/更新版本的人数及下载集中程度,不只是在线人数。客户端当前后台最多 8 张并发,进关优先并暂停新的后台派发,已发出的请求可能与进关请求短暂重叠。并发不增加完整下载一遍的图片总量,但会使请求更集中、提高峰值。有效用户目录文件和升级前的有效 Cocos 缓存可直接复用。总版本号变动会让旧玩家逐步重新下载新版本的整套图片,需要关注发布时的集中流量。
|
||
|
||
CDN 缓存命中时由节点返回,未命中/过期才需要回源,因此不能把玩家的全部图片请求直接等同于源站请求。预热、缓存有效期和监控命中率/回源流量会影响实际负载。[阿里云 CDN 官方说明](https://www.alibabacloud.com/help/en/cdn/getting-started/getting-started)
|
||
|
||
部署时应将 PNG 的 `v` 保留为缓存键,使同版本请求共享缓存、不同版本相互区分;避免为每张 PNG 增加每次变化的时间戳。新版本可在切换 version.json 前预热带新 `v` 的 URL。版本 JSON 仍需保证更新及时,不要直接套用 PNG 的长缓存规则。[腾讯云缓存键规则](https://cloud.tencent.com/document/product/228/47671)
|
||
|
||
10 秒重试是客户端等待间隔,不是服务端限流保证;它比 60 秒更频繁,同一批失败玩家也可能集中重试。大规模发布仍需根据 CDN 的请求数、峰值带宽、回源量和错误率评估,必要时再引入启动错峰或分批发布。本次未读取实际 CDN 配置/容量、未进行线上压力测试,也未修改远程部署。
|
||
|
||
## 验证
|
||
|
||
调试控制台:
|
||
|
||
```js
|
||
cc.fx.GameConfig.getLoadedBlockImagePaths();
|
||
JSON.parse(cc.sys.localStorage.getItem('block_image_cache_v1'));
|
||
```
|
||
|
||
50 关进入 51 关后,常驻图片路径应为 51+52 关需求的去重集合;转场期间可能暂时包含旧场景图片。底层纹理销毁及 GPU/进程内存变化需真机实测。
|
||
|
||
运行 node --test tools/test-block-assets.cjs;没有项目级 TypeScript 安装时将 TYPESCRIPT_PATH 指向 TypeScript 的 lib/typescript.js。模拟测试覆盖全关卡需求、文件清单、裁剪布局、两关内存窗口、共享/释放/取消、用户目录缺失/损坏/磁盘不足、原子临时文件、旧 Cocos 临时缓存迁移、语义版本更新、断网重启、微信/抖音/浏览器请求,以及两个场景的缓存命中不弹 Loading、远程重试、后台共享下载和逐图来源日志。另覆盖 10 秒网络重试、状态标记与实际文件不一致,以及前台不受后台等待影响。
|
||
|
||
并发测试覆盖 Block 自有队列的后台 8 路、前台总计 10 路、单张完成即补位、同路径去重和提升优先级、失败不阻塞其他 worker、全体结束后才重试、进关暂停派发、切入 GameScene 后自动继续,以及前台失败后恢复。
|
||
|
||
全项目类型检查与本次修改前相比为 178 项既有诊断,没有新增错误。模拟测试不替代实际 CDN 和真机验证;本次没有上传 CDN 或完成真机图片/内存验收。
|
||
|
||
特殊图导出工具 tools/extract-block-images.py 只生成缺失图片,不覆盖已有图片,原图集需另行保留才能运行。此前复核时 92 张特殊图已是索引色 PNG,与工具直接导出的像素存在差异,原因未确认,当前文件保持不变。
|