Skip to content

Lesson 009|active、enabled、destroy 的区别

Tags: #Creator2.x #Node #Component #active #enabled #destroyDifficulty: ⭐⭐☆☆☆


🎯 本课目标

  • 理解 node.activecomponent.enablednode.destroy() 的区别
  • 理解 activeactiveInHierarchy
  • 理解父节点 active=false 为什么会影响子节点
  • 理解什么时候隐藏、什么时候禁用组件、什么时候销毁
  • 为 UI 管理、对象池、性能优化打基础

🧩 上节课回顾

上一课我们讲了 Component 生命周期:

text
onLoad

onEnable

start

update

onDisable

onDestroy

并且记住了:

text
onEnable 注册事件,优先在 onDisable 取消。
不用 update,就不要写空 update。

这一课我们把几个经常混用的概念彻底分清楚:

text
node.active
component.enabled
node.destroy()

💡 先给结论

这三个东西解决的问题完全不同:

text
active   :控制 Node 以及子树是否激活
enabled  :控制某个 Component 是否启用
destroy  :销毁 Node 或 Component

更直观一点:

text
node.active = false

关闭整个节点 / 子树

component.enabled = false

关闭某个组件

node.destroy()

销毁这个节点

所以它们不是同一个层级的东西。


🧠 active:控制节点和子树

active 是 Node 上的属性。

例如:

ts
this.panel.active = false;

它表示:

这个节点自己是否想激活。

但它不仅影响自己,还会影响子节点的最终激活状态。

例如:

text
Panel
├── Title
├── Button
└── RewardList

如果:

ts
Panel.active = false;

那么通常:

text
Panel 不显示
Title 不显示
Button 不响应
RewardList 不显示
挂在这些节点上的组件 update 停止
子节点 activeInHierarchy 变 false

所以 active=false 更像是:

把这一整棵节点树暂时从运行状态中关掉。


🧠 active 和 activeInHierarchy

Creator 里要区分:

text
active
activeInHierarchy

可以这样理解:

text
active:我自己想不想激活
activeInHierarchy:结合父节点以后,我最终是否真的激活

例如:

text
Panel.active = false
Button.active = true

那么:

text
Button.active = true
Button.activeInHierarchy = false

原因是:

Button 自己想激活,但它的父节点 Panel 是关闭的。

所以子节点最终是否激活,不只看自己,还要看父节点。

可以简单理解为:

text
child.activeInHierarchy =
    parent.activeInHierarchy && child.active

如果有多层父节点,则要一路往上判断。


🔍 父节点 active=false,会不会把子节点 active 改成 false?

不会。

例如:

ts
parent.active = false;
child.active = true;

这时通常是:

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

父节点关闭后,引擎不会简单粗暴地把:

ts
child.active = false;

因为如果这么做,父节点重新打开时,子节点原本的状态就丢了。

例如:

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

之后:

ts
Panel.active = true;

引擎需要恢复成:

text
Button 仍然 active = true
RedPoint 仍然 active = false

所以父节点影响的是子节点的 activeInHierarchy,不是强行改掉子节点自己的 active


🧠 enabled:控制某个组件

enabled 是 Component 上的属性。

例如:

ts
this.rotateScript.enabled = false;

它表示:

关闭这个组件本身。

例如:

text
AvatarNode
├── Sprite
├── Button
└── RotateScript

如果:

ts
rotateScript.enabled = false;

那么通常:

text
RotateScript 不 update
RotateScript 的逻辑停止
Sprite 仍然显示
Button 仍然可以点击
AvatarNode 仍然 active

所以 enabled=false 是局部关闭。

它不是隐藏节点,也不是销毁节点。


🔍 update 是否执行,看哪些条件?

一个组件的 update 是否执行,通常看:

text
1. 组件实现了 update
2. component.enabled = true
3. node.activeInHierarchy = true
4. 对象没有被 destroy

所以如果父节点:

ts
Panel.active = false;

子节点虽然:

text
StartButton.active = true
StartButtonScript.enabled = true

但:

text
StartButton.activeInHierarchy = false

因此:

text
StartButtonScript.update 不执行

注意:

不是父节点 update 不调用,导致子节点 update 不调用。 而是子节点的 activeInHierarchy=false,所以它不进入 update 调度。


🧠 active 和 enabled 的区别

可以这样记:

text
active 管 Node
enabled 管 Component

也可以类比:

text
active 是关电闸
enabled 是关某个电器开关

例如:

text
房间 = Node
灯 = Sprite
门铃 = Button
风扇 = RotateScript

如果:

text
node.active = false

相当于房间断电。

灯不亮,门铃不响,风扇也停。

如果:

text
rotateScript.enabled = false

只是风扇关了。

灯还亮,门铃还响。


🧠 destroy:销毁对象

destroyactive / enabled 完全不同。

ts
node.destroy();

表示:

这个节点要被销毁。

销毁后,它不是暂时关闭,而是准备从场景中移除。

如果:

ts
rewardItem.destroy();

那么这个节点以及子节点都会进入销毁流程。

销毁后不应该再继续使用。


🔍 destroy 不是简单的“马上凭空消失”

在 Creator 里,destroy() 通常可以先理解为:

text
destroy()

标记这个对象要销毁

当前帧后续流程中被清理

onDestroy 被调用

对象不应该再被使用

所以写了:

ts
node.destroy();
console.log(node);

变量可能还暂时有引用,但这个对象已经不应该继续当正常节点使用。


🧠 active=false 和 destroy 的区别

active=false

适合:

text
暂时隐藏
后面还会再显示
反复打开 / 关闭的界面
对象池中的对象

例如:

ts
this.panel.active = false;

以后还能:

ts
this.panel.active = true;

恢复使用。


destroy

适合:

text
确定以后不用了
切场景清理
临时特效播放结束
一次性弹窗关闭后释放
列表 item 不再复用

例如:

ts
this.effectNode.destroy();

销毁后就不要再拿它继续操作。


🧠 enabled=false 和 destroy component 的区别

enabled=false

适合:

text
只是暂时关掉某个功能
节点还要显示
其他组件还要工作

例如:

ts
this.rotateScript.enabled = false;

头像还显示,只是不旋转。


destroy component

例如:

ts
this.rotateScript.destroy();

适合:

text
这个组件以后真的不需要了

但实际项目里,一般不建议频繁 destroy / addComponent,因为组件创建和销毁有成本。

更常见的是:

ts
component.enabled = false;

💼 工作中的真实案例

案例一:弹窗关闭

如果是主界面常用弹窗:

text
ShopPanel
BagPanel
TaskPanel

通常不建议每次关闭都 destroy。

更常见:

ts
panel.active = false;

下次打开:

ts
panel.active = true;

这样不用重新创建节点和组件。

但如果弹窗很占内存,且很少打开,也可以考虑销毁并释放资源。


案例二:列表 Item

ScrollView 里有很多 item。

不推荐频繁:

ts
item.destroy();

再:

ts
cc.instantiate(prefab);

因为创建销毁成本高。

更好的方式是:

text
对象池
item.active = false
下次复用 item.active = true
刷新数据

案例三:按钮显示但不能点击

例如一个按钮在冷却中,想让它看得见,但不能点击。

不要写:

ts
buttonNode.active = false;

因为这样按钮会消失。

更合适:

ts
button.getComponent(cc.Button).interactable = false;

或者视情况:

ts
button.getComponent(cc.Button).enabled = false;

一般“按钮显示但不可点击”,优先考虑 interactable=false


案例四:特效播放结束

一次性爆炸特效播放结束后如果不再复用,可以:

ts
effectNode.destroy();

如果这种特效频繁出现,建议使用对象池,而不是频繁 destroy / instantiate。


⚠️ 常见误区

误区一:active=false 就是 destroy

不是。

active=false 是暂时关闭,节点还在。

destroy() 是销毁,节点后续不应该再使用。


误区二:enabled=false 会隐藏节点

不一定。

enabled=false 只关闭某个组件。

例如关闭脚本组件不会让 Sprite 消失。


误区三:destroy 后马上还能安全继续用

不应该。

即使变量还引用着它,也不应该继续操作已经 destroy 的对象。


误区四:为了省性能,所有不用的东西都 destroy

不一定。

频繁 destroy / instantiate 也很耗性能。

对于会反复使用的对象,应该考虑:

text
active=false
对象池
复用

🎤 面试会怎么问

active 和 enabled 有什么区别?

参考回答:

active 是 Node 的激活状态,会影响节点及其子树;enabled 是 Component 的启用状态,只影响当前组件。active=false 会让节点从 activeInHierarchy 中退出,影响显示、事件和 update;enabled=false 通常只关闭该组件的生命周期和功能。


active=false 和 destroy 有什么区别?

参考回答:

active=false 是暂时禁用节点,节点仍然存在,可以再次 active=true 恢复;destroy 是销毁对象,之后不应该再继续使用,通常会触发 onDestroy 并由引擎清理。


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

参考回答:

因为 update 是否执行看的是节点的 activeInHierarchy,而不只是 active。父节点 active=false 后,子节点自己的 active 仍然可以是 true,但子节点 activeInHierarchy 会变成 false,所以挂在子节点上的组件不会进入 update 调度。


为什么列表 item 不建议频繁 destroy?

参考回答:

频繁 destroy 和 instantiate 会带来对象创建、销毁、生命周期调用、内存分配和 GC 压力。对于列表 item,更推荐使用对象池和 active 控制显示隐藏。


📝 今日练习参考答案

题目:

text
ActivityPanel
├── CloseButton
│   ├── Sprite
│   └── Button
├── RewardList
└── CountDownScript

答案:

  1. 如果只是临时关闭整个活动界面,应该用:
ts
ActivityPanel.active = false;
  1. 如果关闭后确定这个界面以后不会再用,可以考虑:
ts
ActivityPanel.destroy();

但真实项目里还要考虑资源释放、引用清理、事件解绑等问题。

  1. 如果 CloseButton 还要显示,但暂时不能点击,不应该关 CloseButton.active,应该关 Button 的交互,例如:
ts
closeButton.getComponent(cc.Button).interactable = false;
  1. 如果只想暂停 CountDownScript 的 update,但界面还要显示,应该:
ts
this.countDownScript.enabled = false;
  1. 列表 item 不建议频繁 destroy / instantiate,因为每次创建和销毁都有节点、组件、生命周期、内存分配、GC 和渲染数据重建成本。更推荐对象池复用。

🚀 能力成长

学完本课后,你应该能:

  • 判断什么时候用 active,什么时候用 enabled,什么时候用 destroy
  • 解释 active 和 activeInHierarchy 的关系
  • 解释父节点 active=false 为什么会影响子节点 update
  • 在 UI 管理中避免错误隐藏、错误销毁、错误禁用
  • 为后续对象池和性能优化建立基础

✅ 本课总结

记住这几句话:

text
active:控制 Node 及其子树。
enabled:控制某个 Component。
destroy:销毁对象。
父节点 active=false,不会改掉子节点自己的 active。
父节点 active=false,会让子节点 activeInHierarchy=false。
update 是否执行,看 activeInHierarchy、enabled、是否实现 update、是否已销毁。
反复打开关闭的对象优先 active=false,不要频繁 destroy。

下一课: Stage01 阶段复习:从 API 到引擎数据流