Skip to content

Stage02|Engine Loop

Lesson011|cc.game 启动后发生了什么?

一、本课目标

理解 Cocos Creator 游戏启动后,引擎大概经历了什么流程。

本课重点不是背源码,而是建立这条主线:

text
cc.game 启动游戏

cc.director 接管场景

加载 Scene

创建 Node 和 Component

执行生命周期

进入主循环

每帧调用 update / lateUpdate

渲染到屏幕

二、cc.game 是什么?

cc.game 可以理解成:

Cocos Creator 游戏运行时的总入口 / 总开关 / 总管家。

它负责:

text
初始化游戏
初始化引擎配置
初始化渲染环境
启动主循环
暂停游戏
恢复游戏
结束游戏

它不是某个节点,也不是某个组件。

你平时写的:

ts
onLoad() {}
onEnable() {}
start() {}
update(dt) {}

不是自己主动执行的,而是被引擎启动后的主循环调度出来的。


三、cc.game 启动后的大流程

游戏启动后,可以粗略理解成:

text
游戏启动

cc.game.run()

初始化引擎配置

初始化渲染环境

初始化资源系统

初始化事件系统

启动 cc.director

加载首场景

创建场景节点树

创建节点上的组件

执行组件生命周期

进入主循环

每帧 tick

核心关系:

text
cc.game:负责游戏启动和主循环
cc.director:负责场景管理和每帧调度
Scene / Node / Component:被调度和管理的对象
Renderer:负责把结果画出来

四、cc.game 和 cc.director 的区别

可以这样记:

text
cc.game:游戏级别
cc.director:场景级别

类比:

text
cc.game 像公司老板
cc.director 像项目经理
Scene 像当前项目
Node / Component 像具体员工

cc.game 负责让整个游戏跑起来。

cc.director 负责让当前场景每帧往前推进。

所以:

ts
cc.game.pause();
cc.game.resume();

是游戏级别的控制。

而:

ts
cc.director.loadScene("Main");

是场景级别的控制。


五、update 是谁调用的?

你写的代码:

ts
update(dt) {
    this.node.x += 10;
}

不是自己运行的。

更准确的流程是:

text
cc.game 启动游戏主循环

每帧驱动 cc.director.mainLoop

director / scheduler / component scheduler 处理每帧调度

找到符合条件的 Component

调用组件上的 update(dt)

所以 update 是组件的方法,不是 Node 的方法。

Node 本身没有业务 update,真正被调用的是挂在 Node 上的 Component。


六、组件的 update 想执行,需要满足什么条件?

一个组件的 update(dt) 能被执行,通常需要满足:

text
节点在场景树中
节点 activeInHierarchy = true
组件 enabled = true
组件实现了 update 方法
游戏主循环正在运行

也就是说,不是你写了 update,它就一定会执行。

它必须处在引擎允许调度的状态里。


七、父节点 active=false,子节点 active=true,为什么子节点 update 不执行?

重点概念:

text
active
activeInHierarchy

active 表示节点自己想不想激活。

activeInHierarchy 表示节点在整个场景树中实际是否激活。

例如:

text
parent.active = false
child.active = true

虽然子节点自己的 activetrue,但是因为父节点关闭了,所以:

text
child.activeInHierarchy = false

于是子节点上的组件不会被 update 调度。

正确理解是:

text
不是因为父节点 update 不执行,所以子节点 update 不执行
而是因为父节点 inactive 导致子节点 activeInHierarchy=false
所以子节点上的组件不再满足 update 调度条件

八、生命周期顺序

场景加载后,节点和组件创建完成,引擎会执行组件生命周期。

常见顺序:

text
onLoad

onEnable

start

update

lateUpdate

记忆方式:

text
onLoad:我出生了
onEnable:我可以工作了
start:正式开始前准备一次
update:每帧工作
lateUpdate:每帧收尾

九、onLoad、onEnable、start 的区别

1. onLoad

适合做基础初始化。

ts
onLoad() {
    this.hp = 100;
    this.score = 0;
}

理解:

text
组件被加载进来了,我先准备自己的基础数据。

2. onEnable

当组件进入可用状态时触发。

ts
onEnable() {
    cc.log("组件启用");
}

如果节点或组件从关闭变成开启,可能再次触发 onEnable

理解:

text
我现在可以参与工作了。

3. start

start 通常在第一次 update 前执行一次。

它适合做需要等其他组件也初始化后再进行的逻辑。

ts
start() {
    this.refreshUI();
}

理解:

text
大家都差不多准备好了,我开始正式工作。

十、一帧里大概发生什么?

游戏进入主循环后,每一帧大概会经历:

text
处理输入

处理定时器 / scheduler

执行 update

执行 lateUpdate

处理动画 / action

更新节点变换

收集渲染数据

合批

提交 GPU

显示到屏幕

这也是之前 Dirty Flag 和延迟更新能接上的地方。

例如:

ts
update(dt) {
    this.node.x += 10;
    this.node.y += 20;
    this.node.scale = 2;
}

这不代表 World Matrix 会立刻计算 3 次。

更合理的理解是:

text
修改 x

标记 transform dirty

修改 y

继续标记 dirty

修改 scale

继续标记 dirty

本帧后续阶段

统一计算 transform / world matrix

统一参与渲染

业务代码主要负责改状态。

引擎会在合适的阶段统一处理这些状态变化。


十一、component.enabled、node.active、cc.game.pause 的区别

1. component.enabled = false

ts
this.getComponent(MyScript).enabled = false;

影响的是:

text
这个组件自己

结果:

text
这个组件不再执行 update
但节点可能仍然显示
节点上的其他组件可能仍然工作
子节点不受它影响

一句话:

text
enabled 管组件。

2. node.active = false

ts
this.node.active = false;

影响的是:

text
这个节点以及它的子节点

结果:

text
当前节点不再 activeInHierarchy
子节点也会因为父节点关闭而 activeInHierarchy=false
相关组件不再 update
节点通常不再渲染

一句话:

text
active 管节点树的一段。

3. cc.game.pause()

ts
cc.game.pause();

影响的是:

text
整个游戏主循环

结果:

text
整个游戏的每帧推进暂停
update 不继续跑
dt 不继续推进
游戏整体暂停

一句话:

text
pause 是全局暂停。

十二、本课核心总结

cc.game 启动后,不是直接进入 update

更完整的过程是:

text
cc.game 启动游戏

初始化引擎和渲染环境

cc.director 加载首场景

创建 Scene / Node / Component

执行 onLoad

执行 onEnable

执行 start

进入 mainLoop

scheduler / component scheduler 找到可执行组件

调用组件的 update(dt)

后续处理 transform / render / draw

最终要记住一句话:

cc.game 负责让游戏跑起来,cc.director 负责让场景每帧往前推进,组件生命周期和 update 都是被引擎主循环调度出来的。


十三、本课过关答案

1. cc.game 和 cc.director 分别负责什么?

text
cc.game 负责游戏启动、暂停、恢复和主循环。
cc.director 负责场景管理和每帧调度。

2. 谁调用 update?

text
cc.game 启动主循环,每帧驱动 cc.director.mainLoop。
director / scheduler / component scheduler 找到符合条件的组件,然后调用组件的 update(dt)。

3. 父节点 active=false,子节点 active=true,子节点 update 会执行吗?

text
不会。
因为父节点 active=false 后,子节点虽然自己的 active 仍然可以是 true,
但是它的 activeInHierarchy 会变成 false。
所以子节点上的组件不满足 update 调度条件,update 不会执行。

4. 生命周期顺序是什么?

text
onLoad → onEnable → start → update → lateUpdate

5. component.enabled=false 和 node.active=false 的区别是什么?

text
component.enabled=false 只影响这个组件。
node.active=false 影响这个节点以及它的子节点。

6. cc.game.pause() 和 node.active=false 的区别是什么?

text
cc.game.pause() 是全局暂停游戏主循环。
node.active=false 是局部关闭某个节点和它的子节点。

7. 修改 x、y、scale 会立刻计算 3 次 World Matrix 吗?

text
通常不会。
这些修改主要会标记 dirty,真正的 transform / world matrix 计算会在本帧后续阶段统一处理。

8. 一句话总结 cc.game 启动后到 update 被调用的过程

text
cc.game 启动游戏并初始化引擎和渲染环境,
cc.director 加载首场景,创建节点和组件,
执行 onLoad / onEnable / start,
然后在 mainLoop 中通过调度系统调用符合条件组件的 update(dt)。

十四、下一课预告

下一课进入:

text
Stage02|Engine Loop
Lesson012|Director.mainLoop:一帧到底怎么跑?

下一课会继续拆:

text
一帧的开始

scheduler

update

lateUpdate

transform

render

下一帧

重点理解:

为什么你的业务代码不是“立刻生效到屏幕”,而是被一帧一帧地推进和统一处理。