Skip to content

Stage02|Engine Loop ​

Lesson018|active、activeInHierarchy、enabled 如何影响调度? ​

Tags: #Creator2.x #Stage02 #active #activeInHierarchy #enabledDifficulty: ⭐⭐⭐⭐☆


一、本课目标 ​

Stage01 已经区分过三个状态,本课把它们放回 Engine Loop:

一个节点或组件状态改变后,调度系统在本帧和后续帧如何响应?

完成本课后,你应该能够:

  • 解释 active、activeInHierarchy、enabled 的层级差异。
  • 判断父节点关闭时子节点为什么不再 update。
  • 解释同一帧启用、禁用和销毁的工程风险。
  • 选择局部停用、整棵节点树停用或全局暂停。

二、三个状态分别表示什么 ​

text
Node.active:节点自身的意愿
Node.activeInHierarchy:结合父节点后的最终状态
Component.enabled:某个组件自己的工作开关

例如:

text
Root.active = true
Panel.active = false
Button.active = true

结果是:

text
Panel.activeInHierarchy = false
Button.activeInHierarchy = false

但 Button.active 自己仍然可以是 true。父节点没有替它改写本地属性,只改变了它在场景树中的最终状态。


三、update 的调度条件 ​

一个组件的 update 想被正常调用,通常要满足:

text
组件实现 update
组件 enabled = true
节点 activeInHierarchy = true
对象没有待销毁
游戏主循环正在推进

因此下面的状态:

text
child.active = true
child.enabled = true
parent.active = false

仍然不能让 child 的 update 执行,因为:

text
child.activeInHierarchy = false

四、关闭父节点时发生什么 ​

ts
this.panel.active = false;

可以从传播角度理解:

text
Panel.active 变为 false
    ↓
Panel.activeInHierarchy 变为 false
    ↓
子节点 activeInHierarchy 递归变化
    ↓
相关组件收到禁用状态变化
    ↓
后续 update / 事件 / 渲染不再按启用状态进行

重点是:

不是因为父节点的 update 不执行,所以子节点的 update 不执行;而是子节点最终不再满足调度条件。


五、enabled 和 active 的差异 ​

只关闭组件 ​

ts
this.countDown.enabled = false;

影响:

text
countDown 不再 update
countDown 的生命周期状态发生变化
Node 仍可能显示
同节点其他组件仍可能工作
子节点不因它单独关闭

关闭节点 ​

ts
this.panel.active = false;

影响:

text
Panel 和子树 activeInHierarchy 变为 false
相关组件停止正常调度
渲染和交互通常停止
节点对象仍然存在,可以重新 active

六、同一帧内改变状态的边界 ​

假设:

ts
update() {
    this.enabled = false;
    this.node.x = 100;
    cc.log('after disable');
}

通常不能把 enabled = false 理解为“当前 JavaScript 函数立刻被强行中断”。当前函数已经进入执行,代码会继续执行到函数返回;组件是否进入后续调度,要看引擎下一次检查和当前阶段。

工程上不要依赖这种边界写复杂逻辑。更清晰的写法是:

ts
update(dt: number) {
    if (this.shouldStop) {
        this.enabled = false;
        return;
    }

    this.runFrame(dt);
}

七、重新启用节点会发生什么 ​

ts
this.panel.active = true;

如果父节点也激活,子树可能重新获得:

text
activeInHierarchy = true
    ↓
相关组件 onEnable
    ↓
重新进入可调度状态
    ↓
后续 update 恢复

但以下内容不会自动重置:

  • 业务字段。
  • 倒计时剩余值。
  • 列表滚动位置。
  • 外部服务状态。

所以“重新 active”不是“重新创建一个全新的对象”。需要重置数据时,应提供显式的 reset 或 open 方法。


八、active、enabled、interactable 不是一层 ​

按钮相关状态经常混在一起:

text
node.active = false
    → 节点树不可见、不可正常交互

button.enabled = false
    → Button 组件停止工作

button.interactable = false
    → 按钮通常仍显示,但不可点击

具体视觉效果还可能受 Transition、Sprite、脚本和父节点影响。排查“按钮点不了”时应该逐层检查,而不是只改一个布尔值。


九、案例:弹窗三种关闭方式 ​

方式 A:反复打开关闭 ​

ts
this.popup.active = false;

适合频繁使用的弹窗。对象保留,打开时恢复 active。

方式 B:只暂停倒计时 ​

ts
this.countDown.enabled = false;

适合弹窗仍需显示,但某个逻辑暂时停止。

方式 C:确定不再使用 ​

ts
this.popup.destroy();

适合一次性对象或确定不会复用的节点,但要处理引用、事件和资源关系。


十、常见误区 ​

误区一:父节点 active=false 会把子节点 active 属性改成 false ​

通常不会。改变的是子节点的 activeInHierarchy。

误区二:activeInHierarchy 可以直接当作配置写入 ​

它是结合层级计算出的最终状态,通常用于读取和判断。

误区三:enabled=false 会隐藏组件对应的 Sprite ​

脚本组件禁用不等于 Sprite 组件禁用。要隐藏显示,应控制节点或渲染组件。

误区四:重新 active=true 会重新执行 start ​

不要依赖这一点。需要每次打开执行的逻辑放在 onEnable 或显式方法。

误区五:关闭根节点就是全局暂停 ​

根节点之外的 Scheduler、网络、音频和常驻系统可能继续工作。


十一、练习与答案 ​

练习 ​

给出状态:

text
Root.active = true
Panel.active = false
Button.active = true
ButtonScript.enabled = true

回答:

  1. Button.active 是什么?
  2. Button.activeInHierarchy 是什么?
  3. ButtonScript.update 会执行吗?
  4. 如果只将 Panel.active=true,可能恢复什么?
  5. 如果只将 ButtonScript.enabled=false,Button 是否一定消失?

参考答案 ​

  1. true。
  2. false,因为 Panel 没有激活。
  3. 不会,因为节点不满足 activeInHierarchy 条件。
  4. Button 的 activeInHierarchy、显示、交互和脚本调度可能恢复,前提是其他状态正常。
  5. 不一定。只关闭脚本组件,Button 或 Sprite 组件仍可能显示。

十二、Creator 2.4.x 可复现实验:状态矩阵 ​

准备父节点 Panel、子节点 Button 和 ButtonScript,每隔 0.5 秒打印:

ts
cc.log({
    active: this.node.active,
    activeInHierarchy: this.node.activeInHierarchy,
    enabled: this.enabled,
    valid: this.node.isValid
});

依次执行:Panel.active=false、Panel.active=true、Button.active=false、ButtonScript.enabled=false、ButtonScript.enabled=true。把每一步的状态和 update 日志填入表格。预期 activeInHierarchy 是父链最终结果;关闭脚本不必隐藏节点;销毁后不能再把对象当作可用实例。

典型故障 ​

“按钮看得见但点不了”先检查 Button 组件和 activeInHierarchy,再查遮挡和事件;“脚本不更新但节点可见”检查脚本 enabled、父节点 active 和全局 pause;“重新打开后状态错乱”检查是否只改了 active,却没有恢复业务数据或重新注册监听。

十三、版本边界 ​

active、activeInHierarchy、enabled 和 isValid 是 Creator 2.4.x 的公开状态入口。状态变更何时从当前调度队列移除、同帧剩余组件是否继续,属于具体调度实现;业务代码不应依赖“关闭后这一帧一定还会执行几次”。

十四、应用题与推导答案 ​

题目: 对象池回收一个敌人时,应该销毁节点还是设为 inactive?

答案: 对象池通常设为 inactive 以保留实例和资源,取出时再 active 并重置速度、血量、事件和定时器;真正永久移除且没有复用计划时才 destroy。两种操作都不能替代业务状态重置和监听清理。

十五、本课总结 ​

text
active 是 Node 自身状态。
activeInHierarchy 是层级传播后的最终状态。
enabled 是 Component 自身状态。
父节点关闭会让子节点失去最终激活状态。
重新 active 不等于重建对象。
局部关闭、组件关闭、销毁和全局暂停必须分层选择。

十六、下一课预告 ​

text
Lesson019|schedule、Action、Tween、Animation 的时间由谁推进?