跳到主要内容

游戏循环、异步与时间

级别:首作必学。 客户端在有限帧预算里接收输入、更新状态、提交渲染。一次长任务会阻塞这条链,玩家看到的是点击没有反应或画面停顿。

帧循环与按时间移动​

Cocos 的 update(dt) 在组件更新时调用,dt 是时间间隔,单位为秒。移动速度应与时间相乘:

// Cocos 组件内:每秒移动 120 个本地坐标单位。
update(dt: number) {
const p = this.node.position;
this.node.setPosition(p.x + 120 * dt, p.y, p.z);
}

这只能演示按时间移动。对于物理碰撞或确定性模拟,还需考虑固定步长、长帧和同步策略。翻牌项目的规则按事件改变,不需要每帧扫描所有牌。

60 FPS 对应约 16.7 毫秒一帧,是一个理解预算的例子,不是所有设备必须或能够达到的承诺。需要同时观察帧耗时分布和长帧,平均 FPS 不足以证明手感稳定。

单线程仍有逻辑竞争​

旧局点击两张牌 → 开始延迟盖回
↓
玩家重开新局
↓
旧回调完成 → 错误修改新局

处理方法有两层:先取消自己拥有的任务;不能取消或取消结果不确定时,再核对任务归属。例如请求开始时记录局 ID,完成后只在 ID 仍一致且节点有效时应用结果。

const requestedRound = this.roundId;
const result = await fetchLevel(); // 项目自己提供的请求函数
if (requestedRound !== this.roundId) return;
// 此处还应确认组件未销毁,再应用 result。

不要用 await 等待时阻断整个界面的设计思路;它让出执行权,但状态仍会在等待期间变化。

时间不是只有一种​

时间适合用途边界
帧间隔或单调计时动画、局内耗时暂停期间是否计入需明确定义
系统日期时间展示日期、日志时间用户可调整,不适合可信奖励
服务端时间可信活动与奖励需要网络和失败处理

切后台后,下一帧不能简单补算一大段时间。翻牌配对可暂停操作和局内计时,恢复时继续未完成的判定;有离线收益的游戏则要单独定义计算和可信边界。

生命周期与所有权​

谁创建监听、计时器、Tween 和资源引用,谁负责在退出时结束或交还它。组件停止可交互时解除输入监听,销毁时清理长寿命外部服务的回调。具体示例见组件生命周期。

练习:在等待盖回时连续重开三次,并在资源加载时退出场景。完成标准:没有旧操作影响新状态,没有访问已销毁节点的异常,事件不会随重开次数增加。

参考:Cocos 生命周期。