《Cocos 游戏开发入门》10.3 引用计数与资源释放
资源拉进来了,还得知道什么时候放它走:用引用计数跟踪谁在用一份资源,实现 retain/release 与零引用回收,讲清循环引用为何会泄漏、如何排查,再补一层 LRU 缓存兜住内存上限;末节对照 Cocos Creator 3.8 的 addRef / decRef / releaseAsset。
category
资源拉进来了,还得知道什么时候放它走:用引用计数跟踪谁在用一份资源,实现 retain/release 与零引用回收,讲清循环引用为何会泄漏、如何排查,再补一层 LRU 缓存兜住内存上限;末节对照 Cocos Creator 3.8 的 addRef / decRef / releaseAsset。
清单能列出要拉哪些资源,接下来把它们真正并发拉进来:写一个限制并发数的 Promise 池,按字节加权汇报总进度,区分首屏预加载与按需加载,再加失败重试与降级占位;末节对照 Cocos Creator 3.8 的 resources.load 与 preload。
把散落的图片、音频、图集收进一份清单:定义 manifest 数据结构与 UUID 命名,用依赖闭包算出「加载一个资源要连带拉哪些」,再做拓扑排序得到可预加载顺序,最后给出 Cocos Creator 3.8 的 Bundle 与 resources 对照。
前两节的音频在桌面浏览器跑得好好的,一上手机就出问题:首次进游戏没声音、切到后台再回来 BGM 断了、iOS 静音键开着就完全没声。本节讲清浏览器的自动播放策略,用首次用户交互解锁 AudioContext,用 visibilitychange 处理切后台暂停与回前台恢复,并给出 Cocos Creator 3.8 的对照。
上一节解决了音效的并发问题,但一条 BGM 加几十条音效,音量该怎么统一控制?本节给 mini2d 搭出主音量/BGM/音效三级总线,讲清 gain 相乘的数学,实现 BGM 交叉淡入淡出与切歌,并给出静音状态与用户偏好的 localStorage 持久化;末节对照 Cocos Creator 3.8。
画面之外,《方块冲刺》还缺声音。本节给 mini2d 写出音频层:用 HTMLAudioElement 封装播放实例,用对象池复用元素避免反复创建,处理同时播放上限与最旧/最轻抢占策略,并给出统一的 play/stop/setVolume 接口;末节对照 Cocos Creator 3.8 的 AudioSource、AudioClip 与 playOneShot。
本节把帧动画与补间收束成由状态驱动的表现层:定义 idle、run、jump、hurt 四态与转移规则,用权重做过渡混合、优先级处理打断;再手写 HeroPresenter 接进 mini2d,最后对照 Cocos Creator 3.8 的 crossFade、AnimationState、SkeletalAnimation 与 ParticleSystem2D。
本节从插值公式出发讲清补间:先把时间归一化成进度,再用缓动函数把进度重映射;给出线性、二次、三次、正弦、指数、圆周、回弹、弹跳九族缓动的公式与纯 TypeScript 实现,并手写一个支持链式与并行的补间队列,最后对照 Cocos Creator 3.8 的 tween、Tween 与 TweenEasing。
本附录按报错原文索引《Cocos 游戏开发入门》的高频问题,每条给「现象 / 原因 / 修法」:固定步长累积器丢步跳步、window 变量 undefined 因 ES module 被 file:// 拦、Playwright 截图全白、纹理跨域污染 canvas、DPR 坐标错位、tsc 报 cc 无导出成员、YAML 里 @ 开头列表项、Hugo REF_NOT_FOUND 与围栏未闭合。
本附录固定《Cocos 游戏开发入门》的环境口径:Node 26.5.0 的安装与校验、npm 与 pnpm 的源切换、/tmp/cocosbook 工作目录的 package.json 与依赖清单、tsconfig / vitest / vite 三份配置全文、Node 26 的可擦除语法约束、Playwright 安装与截图脚本,以及断网时的零网络复现步骤与一分钟自检。
本附录速查 Cocos Creator 3.8.8 工程结构:assets、library、temp、build、settings 的职责,.scene、.prefab、.meta 与 settings/v2/packages/*.json 的角色,并给出接入 @cocos/creator-types 做离线类型检查、用 cc.d.ts 反查 API 的方法,以及哪些需编辑器、哪些可命令行复现。
本附录是全书 mini2d 运行时的 API 速查表:按模块列出 TimeSource、realTime、FakeTime、Clock、Color、rgba、DrawCommand、Renderer、RecordingRenderer、CanvasRenderer 与 Game 的真实类型签名,并给出「假时钟推进固定步数 + 记录式渲染器断言绘制指令序列」的确定性测试写法,方便随手回查。
本节把图集里的一格格精灵接成会动的帧动画:用时间轴把已播放秒数换算成帧索引,实现单次、循环与乒乓三种播放模式,把帧率与播放速度解耦,并加入事件帧回调;再用纯 TypeScript 写出 FramePlayer,最后对照 Cocos Creator 3.8 的 Animation、AnimationClip 与 AnimationState。
画 100 个精灵不等于 100 次 Draw Call。本节讲透批处理的三个条件——同纹理、同材质、命令连续,用记录式渲染器把 Draw Call 数下来,量化纹理切换的代价,说明图集为何是合批的前提,并给出 mini2d 实现、vitest 测试与 Cocos Creator 3.8 的 Sprite、SpriteAtlas 与动态合图对照。
本节把碰撞检测闭环成碰撞响应:推导 AABB 的最小平移向量(MTV),让重叠的物体沿最短方向被推出;讲清穿透与隧道效应的成因及扫掠检测解法;定义 isTrigger「只报事件、不推物体」的语义;再用位运算实现碰撞层与掩码过滤;末段对照 Cocos Creator 3.8 的 Collider2D、RigidBody2D、Contact2DType 与 IPhysics2DContact。
场景图只解决了位置,没解决「谁盖住谁」。本节为《方块冲刺》实现绘制顺序:用 layer + zIndex + 遍历序组成稳定排序键,按屏幕空间裁剪框实现滚动视窗与遮罩,并给出 mini2d 的渲染列表、裁剪栈、vitest 测试与 Cocos Creator 3.8 中 Mask、UIOpacity 的对照。
暂停面板、设置面板、确认框可能同时出现,谁盖在谁上面、点击落在谁身上、返回键该关哪个?本节给 mini2d 实现弹窗栈:用 push/pop/replace 管理层级,用遮罩与命中测试决定点击归属,用界面状态机约束屏幕跳转,并统一处理返回键。末节对照 Cocos Creator 3.8 的 BlockInputEvents、PageView 与输入系统。
暂停不是把循环停掉,而是让 dt 归零。本节给《方块冲刺》引入 timeScale:讲清它作用在缩放后的 dt 上、为什么 UI 计时必须不受缩放影响,并用慢动作与命中停顿两个玩法验证整套时间管线,最后对照 Cocos Creator 3.8 的暂停与时间缩放 API。
本节解决《方块冲刺》里「怎么找出该检测哪些物体」的问题:先看清宽相与窄相的分工,算清 O(n²) 逐对比较的代价,再从零实现均匀网格与四叉树两套空间划分,讲清静态层只插一次、动态层每帧重建的分层策略,最后用基准数据对比取舍,并对照 Cocos Creator 3.8 的 PhysicsSystem2D 调试绘制。
《方块冲刺》的排行榜有一千条记录,屏幕只放得下十几条。本节给 mini2d 实现虚拟列表:用前缀和与二分查找算出可视区,只渲染可见项;用节点池复用实例避免反复创建;再补上滚动惯性与越界回弹。末节对照 Cocos Creator 3.8 的 ScrollView、Layout 与 PageView。