Skip to content

Lesson 005|Node 到底是什么?为什么它是 Creator 的核心?

Tags: #Creator2.x #Node #Component #Active #TransformDifficulty: ⭐⭐☆☆☆


🎯 本课目标

  • 理解 Node 在 Creator2.x 中的核心作用
  • 理解 Node 和 Component 的关系
  • 理解为什么 Node 不等于 Sprite、Label、Button
  • 理解 Node 的 active 对显示、事件、脚本和子节点的影响
  • 为后续学习 Component、生命周期、事件系统和性能优化打基础

🧩 上节课回顾

上一课我们讲了 Transform:

text
Transform = position + rotation + scale

Transform 表示一个 Node 在空间中如何摆放。

也就是说:

text
position:放在哪里
rotation:旋转多少
scale:放大或缩小

引擎对开发者暴露 position、rotation、scale,底层再把它们合成为 Matrix,用于计算 World Matrix 和最终渲染。


💡 Node 到底是什么?

在 Cocos Creator 里,Node 可以先理解成:

场景树里的一个“空间对象”。

它主要负责:

text
位置
旋转
缩放
父子关系
激活状态
层级关系
事件挂载
组件容器

但是要注意:

Node 本身不等于 Sprite,不等于 Label,也不等于 Button。

Node 更像是一个“空壳”或者“挂载点”。

真正让它显示图片、显示文字、响应按钮逻辑的是挂在它身上的 Component。


🧠 Node 和 Component 的关系

一句话:

Node 决定“在哪里”,Component 决定“做什么”。

例如一个按钮:

text
StartButton
├── Sprite
├── Button
└── StartGameScript

可以理解为:

text
StartButton:Node,负责位置、层级、active、挂载组件
Sprite:Component,负责显示图片
Button:Component,负责按钮交互和点击状态
StartGameScript:Component,负责开始游戏业务逻辑

所以:

text
Node = 容器
Component = 能力

如果没有 Node,Component 没有地方挂。

如果没有 Component,Node 就只是一个空节点。


🔍 为什么不让 Sprite 自己直接有位置?

你可能会想:

为什么不直接让 Sprite 有 x、y、scale、rotation?

比如:

text
Sprite.x = 100
Sprite.y = 200

看起来也可以。

但问题是,一个游戏对象往往不只有一种能力。

比如一个按钮可能同时需要:

text
位置
图片
点击
动画
音效
红点
业务逻辑

如果全部塞进 Sprite,Sprite 会越来越臃肿。

所以 Creator 采用更通用的设计:

text
Node 负责空间和层级
Component 负责具体能力

例如:

text
ButtonNode
├── Sprite
├── Button
├── Animation
└── RedPointScript

这就是组件化设计。


🧠 Node 的核心职责

1. 负责父子关系

例如:

text
Canvas
└── MainPanel
    ├── CloseButton
    ├── TitleLabel
    └── RewardList

这是一棵 Node 树。

父子关系决定:

text
谁跟着谁移动
谁受谁的 scale 影响
谁受谁的 active 影响
谁在层级上属于谁

这也是 World Matrix 的基础。


2. 负责 Transform

Node 保存:

text
position
rotation
scale
anchor

这些决定了它在父节点坐标系里的位置和变换。

例如:

ts
this.node.x = 100;
this.node.scale = 2;

本质上都是在修改 Node 的 Transform。


3. 负责 active 状态

Node 有:

ts
node.active = false;

它表示:

这个节点以及它下面的整棵子树是否处于激活状态。

如果父节点 active = false,那么它下面的子节点通常也不会继续正常显示和执行相关逻辑。

这可以理解为:

text
active 是树状传播的

4. 负责挂载 Component

Node 自己通常不做具体业务。

它通过 Component 获得能力:

ts
this.node.addComponent(cc.Sprite);
this.node.addComponent(cc.Label);
this.node.addComponent(MyScript);

所以:

text
Node 是容器
Component 是能力

5. 负责事件

在 Creator 里,你经常写:

ts
this.node.on(cc.Node.EventType.TOUCH_END, this.onClick, this);

事件也是挂在 Node 上的。

所以 Node 不只是空间对象,也是事件系统中的重要节点。


🔍 active = false 会影响什么?

假设有一个节点:

text
StartButton
├── Sprite
├── Button
└── StartGameScript

当执行:

ts
StartButton.active = false;

可以理解成:

这个节点以及它下面的整棵子树都被关闭了。

它会影响几个方面。


1. Sprite 不再显示

因为节点 inactive 后,渲染组件不会继续参与正常渲染。

text
Sprite 不显示图片

2. Button 不再响应点击

节点 inactive 后,事件系统通常不会再把点击事件分发给它。

text
Button 不可点击

3. StartGameScript 的 update 等逻辑通常不会继续执行

如果脚本里有:

ts
update(dt) {
    // 每帧逻辑
}

当节点 inactive 后,这个组件一般不会继续执行正常的 update

这也是为什么隐藏复杂界面时,用 active = false 可以减少 CPU 开销。


4. 子节点也会受到影响

例如:

text
StartButton
├── Icon
├── Text
└── RedPoint

当:

ts
StartButton.active = false;

下面的 Icon、Text、RedPoint 也会受到影响。


🧠 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 关闭了,所以它在场景中最终还是不激活。

这个概念后面会在 Lesson009 继续深入。


💼 工作中的真实案例

案例一:为什么节点很多会卡?

每个 Node 都可能带来:

text
Transform 计算
active 状态检查
事件管理
组件生命周期
渲染数据更新

即使它不显示图片,只要它在节点树里,也可能有管理成本。

所以优化 UI 时,不要只看图片大小,也要看:

text
Node 数量
Component 数量
层级深度
是否频繁 active 切换
是否频繁修改 Transform

案例二:为什么不要滥用空节点?

空节点很方便。

例如:

text
Root
└── Panel
    └── Content
        └── ItemRoot
            └── IconRoot
                └── Icon

这样层级清晰,但如果过度拆分,节点树会变深。

节点树越深:

text
World Matrix 传播越复杂
active 传播越复杂
查找节点越麻烦
管理成本越高

所以空节点要用,但不要滥用。


案例三:为什么 getChildByName 不适合频繁调用?

例如:

ts
let icon = this.node.getChildByName("Icon");

偶尔用可以。

但如果在 update() 里频繁查找,就会有问题。

因为它需要在子节点列表里查找。

更好的做法是在属性面板绑定:

ts
@property(cc.Node)
icon: cc.Node = null;

或者在 onLoad 缓存:

ts
this.icon = this.node.getChildByName("Icon");

核心思想:

节点树是结构,不要每帧在结构里反复搜索。


⚠️ 常见误区

误区一:Node 就是显示对象

不是。

Node 本身不一定显示。

只有挂了 Sprite、Label、Graphics 等渲染组件,它才会产生显示内容。


误区二:空节点没有成本

不是。

空节点虽然不渲染,但仍然有层级、Transform、active、组件管理等成本。


误区三:Component 可以脱离 Node 存在

在 Creator 中,Component 通常必须挂在 Node 上。

Component 是 Node 的能力扩展。


误区四:节点越细越好

不一定。

合理拆分可以让结构清晰。

但过度拆分会增加节点数量和层级深度。


🎤 面试会怎么问

问题一:Node 和 Component 的区别是什么?

参考回答:

Node 负责空间、层级、Transform、active 和事件等基础能力;Component 负责具体功能,比如显示图片、显示文字、播放动画、处理业务逻辑。Node 是容器,Component 是能力。


问题二:为什么 Creator 采用 Node + Component,而不是继承一堆类?

参考回答:

组件化设计比继承更灵活。一个节点可以通过挂载不同组件组合出不同能力,避免类继承层级过深,也更适合编辑器可视化配置。


问题三:为什么节点数量过多会影响性能?

参考回答:

因为每个节点都可能参与 Transform 更新、active 状态传播、组件生命周期管理、事件管理和渲染数据更新。即使空节点不产生 DrawCall,也会有 CPU 管理成本。


问题四:active 和 activeInHierarchy 的区别是什么?

参考回答:

active 表示节点自身是否希望激活;activeInHierarchy 表示考虑父节点状态后,节点在场景树中最终是否真的激活。父节点 inactive 时,子节点即使 active = true,activeInHierarchy 也会是 false。


📝 今日练习

假设有一个按钮节点:

text
StartButton
├── Sprite
├── Button
└── StartGameScript

回答:

  1. StartButton 是 Node 还是 Component?
  2. Sprite 的职责是什么?
  3. Button 的职责是什么?
  4. StartGameScript 的职责是什么?
  5. 如果 StartButton.active = false,这些组件大概会受到什么影响?

📝 今日练习参考答案

  1. StartButton 是 Node。

  2. Sprite 是 Component,负责显示图片。

  3. Button 是 Component,负责按钮交互、点击状态和按钮行为。

  4. StartGameScript 是自定义 Component,负责开始游戏相关的业务逻辑。

  5. 如果 StartButton.active = false

text
Sprite 不再显示
Button 不再响应点击
StartGameScript 的 update 等逻辑通常不会继续执行
子节点也会受到影响
整个节点从 activeInHierarchy 的激活树中退出

🚀 能力成长

学完这一课后,你应该具备以下能力:

  • 能解释 Node 的核心职责
  • 能解释 Node 和 Component 的区别
  • 能理解为什么 Creator 使用组件化设计
  • 能解释 active 对显示、点击、脚本 update、子节点的影响
  • 能初步分析节点数量、层级深度、空节点过多带来的性能问题
  • 能理解为什么频繁 getChildByName 不适合放在 update 中

✅ 本课总结

记住六句话:

text
Node 是场景树里的空间对象。
Node 负责位置、旋转、缩放、父子关系、active、事件和组件挂载。
Component 是挂在 Node 上的能力。
Node 决定“在哪里”,Component 决定“做什么”。
active 会影响节点自身和整棵子树。
空节点不渲染,但不代表没有成本。

下一课: Component 是什么?为什么逻辑要挂在组件上?