SLG 项目的主城、地图、背包、英雄列表通常包含大量头像、图标和品质框。如果只关注 PNG 文件大小而忽略图集批次、GPU 纹理内存及资源释放很容易出现打开英雄列表时瞬间掉帧DrawCall 随列表项数量快速增长连续切换界面后内存只升不降中低端微信设备闪退首包过大、首屏加载过慢。本文给出一套适用于 Cocos Creator 3.8 的工程化解决思路。一、先区分三个容易混淆的概念1. 图片文件体积指 PNG、JPG、WebP 等文件在磁盘或网络传输中的大小主要影响微信小游戏包体CDN 下载耗时本地缓存占用首屏加载速度。2. GPU 纹理内存图片解码并上传 GPU 后占用通常不能再按 PNG 文件大小计算。例如一张2048 × 2048的 RGBA8888 纹理2048 × 2048 × 4 ≈ 16 MiB即使它压缩后的 PNG 只有 800 KB运行时仍可能消耗约 16 MiB 的纹理内存。3. 图集 Atlas图集把多张小图打进同一张大纹理主要作用是减少纹理切换和渲染批次。Cocos Creator 对 Sprite Atlas 的定义也强调了合并碎图、提高渲染效率的用途。需要注意图集主要解决 DrawCall不等于一定减少纹理内存。如果为了几个小图加载了一张巨大的图集反而可能造成内存浪费。二、SLG 项目的纹理应该如何分层不建议简单地把所有 UI 图片塞进一张大图集。更合理的方式是按照使用场景和生命周期划分。1. 常驻公共图集例如ui_common ├── 按钮底图 ├── 通用关闭按钮 ├── 品质边框 ├── 货币图标 └── 通用页签这些资源会被多个界面反复使用可以常驻内存。2. 功能级图集例如ui_hero ui_bag ui_alliance ui_city_building ui_world_map进入对应系统时加载退出后根据复用频率决定是否释放。3. 动态内容纹理例如武将头像兵种头像联盟旗帜活动运营图世界地图地块玩家自定义图片。这类资源数量大、变化频繁不适合全部打进一个静态大图集通常应采用分包或远程资源按需加载LRU 缓存分辨率分档离开场景后批量释放。三、为什么“已经打图集”DrawCall 还是很高图集只是合批的必要条件之一。两个 UI 元素要进入同一批次通常还需要满足使用相同纹理使用相同材质混合状态一致渲染顺序连续中间没有打断批次的组件。常见错误节点顺序头像图集 Sprite 普通 Label 头像图集 Sprite Mask 头像图集 Sprite即使三个头像来自同一张图集Label、Mask 或自定义材质仍可能打断合批。推荐调整头像背景层 头像 Sprite 层 品质框 Sprite 层 文字 Label 层 特效层将相同渲染状态的节点尽量连续排列。Cocos Creator 也提供运行时动态合图机制将符合条件的小纹理合入动态 Atlas其核心目标同样是减少纹理切换和 DrawCall。 但动态合图存在运行时拷贝成本不能代替合理的静态图集规划。四、SLG 英雄列表的典型错误假设英雄列表同时显示 30 个英雄30 个头像 30 个品质框 30 个兵种图标 30 个等级底图 30 个红点如果每种图片来自不同纹理理论上可能产生大量纹理切换。更严重的是很多项目会在滚动时不断执行resources.load(...)这样会带来重复发起资源查询创建大量临时回调SpriteFrame 切换频繁列表节点频繁创建和销毁GC 抖动。正确做法应当是列表项节点池化进入界面时预加载公共图集头像按需加载并缓存滚出可视区域时取消旧请求退出功能时统一处理缓存生命周期。五、TypeScript防止列表头像异步串图高速滚动时旧的加载请求可能晚于新请求返回导致 A 英雄的头像显示在 B 英雄节点上。可以通过请求版本号解决import { _decorator, Component, Sprite, SpriteFrame, assetManager, } from cc; const { ccclass, property } _decorator; ccclass(HeroAvatarItem) export class HeroAvatarItem extends Component { property(Sprite) private avatarSprite: Sprite | null null; private requestVersion 0; public async setHero(heroId: number): Promisevoid { const currentVersion this.requestVersion; const path hero/avatar/${heroId}/spriteFrame; try { const bundle assetManager.getBundle(hero-assets) ?? await this.loadBundle(hero-assets); const frame await this.loadSpriteFrame(bundle, path); // 节点已被复用丢弃过期结果 if (currentVersion ! this.requestVersion) { return; } if (this.avatarSprite) { this.avatarSprite.spriteFrame frame; } } catch (error) { if (currentVersion ! this.requestVersion) { return; } console.error(英雄头像加载失败heroId${heroId}, error); } } public resetItem(): void { this.requestVersion; if (this.avatarSprite) { this.avatarSprite.spriteFrame null; } } private loadBundle(name: string): Promiseany { return new Promise((resolve, reject) { assetManager.loadBundle(name, (error, bundle) { if (error) { reject(error); return; } resolve(bundle); }); }); } private loadSpriteFrame(bundle: any, path: string): PromiseSpriteFrame { return new Promise((resolve, reject) { bundle.load(path, SpriteFrame, (error: Error | null, frame: SpriteFrame) { if (error) { reject(error); return; } resolve(frame); }); }); } }这段代码解决了什么避免列表项复用后出现头像串位将功能资源放入独立 Bundle为后续缓存和预加载保留扩展点节点回收时通过版本号使旧请求失效。生产项目中还应补充同路径请求合并并发加载数量限制默认头像LRU 缓存Bundle 卸载策略节点销毁检测。六、纹理压缩应该怎么选Cocos Creator 3.8 支持在编辑器中为纹理设置压缩方式并可以同时导出多种格式再由运行环境选择合适资源。官方文档指出PNG、JPG、WebP 一类格式主要减少下载体积ASTC、ETC 等 GPU 压缩格式还能减少运行时纹理内存和显存带宽资源类型推荐策略UI 小图标图集优先控制图集尺寸英雄立绘ASTC 优先并准备兼容回退格式世界地图地表压缩纹理、分块加载、分级清晰度像素风 UI谨慎使用低质量有损压缩文字和细线框注意压缩后边缘伪影Mask、灰度控制图检查通道精度不要盲目压缩ASTC 4×4 与 6×64×4质量更高内存占用也更高6×6压缩率更高适合大面积立绘和背景更大的块体积更小但 UI 边缘、文字和细节容易失真。实践中可以采用英雄立绘ASTC 6×6 重要头像ASTC 4×4 地图地表ASTC 6×6 或 8×8 小尺寸 UI先打图集再进行真机画质验证不要仅在编辑器预览中判断效果应至少覆盖Android 中低端机Android 高端机iPhone微信普通模式微信高性能模式。七、微信小游戏首包与资源包规划微信官方启动优化建议的核心之一是精简首包只保留首屏依赖资源并通过分包加载降低启动阶段的下载量。SLG 可以按以下方式拆分主包 ├── 登录代码 ├── 更新检测 ├── Loading UI ├── 最低限度字体 └── 首屏公共资源 分包city ├── 主城建筑 ├── 主城特效 └── 主城 UI 分包world ├── 世界地图 ├── 行军表现 └── 地图标记 分包hero ├── 英雄界面 ├── 头像 └── 立绘 远程资源 ├── 活动运营图 ├── 新赛季资源 ├── 低频语音 └── 可热更新内容原则首包只回答一个问题玩家看到第一个可交互画面之前哪些资源绝对不可缺少不能因为“以后可能会用”就把完整英雄库、地图素材和活动图片全部放进首包八、资源释放的工程建议不建议// 每关闭一个列表项就立即释放其头像 assetManager.releaseAsset(spriteFrame);因为多个节点可能共享同一个 SpriteFrame、Texture2D 或 Atlas。无统一引用管理时单点释放容易造成其他节点显示异常下一次打开界面重复加载加载与释放反复抖动图集依赖关系难以维护。推荐按功能域释放例如打开英雄系统 → 加载 hero Bundle → 加载英雄公共 UI → 按需缓存头像 关闭英雄系统 → 销毁界面实例 → 保留近期头像缓存 → 超过缓存预算后进行 LRU 淘汰 → 长时间未进入时卸载 hero Bundle对 SLG 而言“退出界面立即全部释放”通常不是最佳策略。合理的短期缓存可以避免玩家在主城、英雄和编队界面之间切换时反复解码纹理。九、优化验收不要只看 FPS建议为每个大型 UI 建立固定指标指标关注点首次打开耗时Bundle、Prefab、纹理解码二次打开耗时缓存是否有效DrawCall图集与节点顺序是否合理纹理内存增量纹理内存增量大图集或立绘是否超预算关闭后内存是否存在长期无法回落滚动最低帧率节点复用和异步加载GC 次数临时对象、闭包、数组分配请求数量是否重复加载同一路径峰值内存加载新图时旧图是否仍未释放推荐分别测试冷启动首次打开连续开关界面 20 次英雄列表高速滚动主城与世界地图反复切换后台切前台低内存设备长时间挂机。十、实战结论SLG UI 纹理优化不是简单的“把图片打成图集”而是四个维度的联合治理网络体积分包、远程资源、WebP 渲染批次静态图集、节点排序、材质统一 运行内存ASTC/ETC、分辨率分档、缓存预算 生命周期Bundle 分域、LRU、节点池、统一释放最终应形成以下工程规范公共图集只放高复用资源功能资源按 Bundle 和生命周期拆分大型立绘使用压缩纹理并准备兼容回退列表节点必须池化异步头像加载必须防止串图资源释放由统一管理器负责所有优化必须在微信真机上用数据验收。参考资料Cocos Creator 3.8压缩纹理微信小游戏启动优化最佳实践Cocos Creator动态合图