外观
Lesson 010|Stage01 阶段复习:从 API 到引擎数据流
Tags: #Creator2.x #Stage01 #Node #Component #Transform #Matrix #LifecycleDifficulty: ⭐⭐⭐☆☆
🎯 本课目标
- 串联 Lesson001~Lesson009 的核心知识
- 从 API 使用思维转向引擎数据流思维
- 理解 Node、Transform、Matrix、Component、Lifecycle 之间的关系
- 能分析
node.x、node.active、component.enabled、destroy()背后的影响 - 完成 Stage01 第一阶段闭环
🧩 本章核心问题
Stage01 一直围绕一个问题:
你在代码里改了一个东西,引擎内部到底发生了什么?
例如:
ts
this.node.x = 100;
this.node.active = false;
this.getComponent(cc.Button).enabled = false;
this.node.destroy();普通开发只知道“效果变了”。
高级开发要知道:
text
数据改了哪里
引擎什么时候处理
影响哪些组件
会不会触发生命周期
会不会影响 update
会不会影响渲染
会不会影响事件
会不会造成性能问题🧠 Stage01 核心知识树
这一章可以拆成五条主线:
text
1. Node 是什么
2. Transform 是什么
3. Matrix 是什么
4. Component 是什么
5. active / enabled / destroy / 生命周期如何影响运行状态它们合起来就是:
text
Node 提供结构和位置
Transform 描述位置、旋转、缩放
Matrix 负责底层变换计算
Component 提供能力
Lifecycle 控制能力什么时候运行🔍 从 this.node.x = 100 开始复盘
你写:
ts
this.node.x = 100;表面看只是改了 x。
但引擎内部大概是:
text
this.node.x = 100
↓
调用 setter
↓
修改 Node 的 position 数据
↓
标记 Transform Dirty
↓
等待本帧合适时机统一更新
↓
重新计算 Local Matrix
↓
根据父节点 World Matrix 计算自己的 World Matrix
↓
影响子节点 World Matrix
↓
渲染组件根据新的 World Matrix 更新顶点数据
↓
Batch / DrawCall
↓
GPU 显示到屏幕所以:
改
node.x不是直接操作屏幕,也不是直接操作 GPU,而是先改 Node 的 Transform 数据。
🧠 为什么有 DirtyFlag?
如果一帧里写:
ts
this.node.x = 100;
this.node.y = 200;
this.node.scale = 2;
this.node.angle = 45;如果每次修改都马上重算矩阵,就可能重算 4 次。
引擎更希望:
text
你改了很多属性
↓
我先记下来:这个节点脏了
↓
这一帧真正需要用的时候
↓
统一算一次这就是 DirtyFlag 的意义:
避免频繁重复计算,把多次修改合并成一次更新。
🧠 Local 和 World
Node 的位置有两种理解:
text
Local:相对父节点
World:最终在世界 / 屏幕中的结果例如:
text
Root.x = 0
Panel.x = 100
Button.x = 30那么:
text
Button.worldX = 130如果:
text
Panel.x = 200那么:
text
Button.worldX = 230Button 自己的 x 没变,但显示位置变了。
更底层一点:
text
Button.worldMatrix = Panel.worldMatrix * Button.localMatrix🧠 Transform 和 Matrix
你平时操作的是:
ts
node.x
node.y
node.angle
node.scale这对开发者直观。
但引擎底层更适合用 Matrix。
所以 Creator 给开发者暴露:
text
position
rotation
scale底层会合成:
text
Local Matrix
World Matrix记住:
text
开发者操作 Transform
引擎使用 Matrix
GPU 最终吃顶点数据🧠 Node 到底是什么?
这一章最重要的一句话:
text
Node 不是图片,不是按钮,不是文字。
Node 是场景树里的空间对象和组件容器。Node 负责:
text
位置
旋转
缩放
父子关系
active
事件
层级
组件挂载但 Node 不负责具体显示。
它能显示,是因为挂了:
text
Sprite / Label / Graphics / Spine它能点击,是因为挂了:
text
Button它能跑业务,是因为挂了:
text
自定义脚本 Component🧠 Component 是什么?
Component 是:
text
挂在 Node 上的能力例如:
text
StartButton
├── Sprite
├── Button
└── StartGameScript职责:
text
Sprite:显示图片
Button:处理交互
StartGameScript:处理业务逻辑所以:
text
Node 决定在哪里
Component 决定做什么🧠 Node + Component 为什么比继承灵活?
如果用继承,很容易变成:
text
ShopRedPointAnimatedButtonWithTimerNode这很难维护。
组件化可以写成:
text
ShopButton
├── Sprite
├── Button
├── Animation
├── RedPointScript
├── CountDownScript
└── ShopButtonScript优势是:
text
按能力组合
职责清晰
组件可复用
编辑器可配置
避免继承爆炸代价是:
text
组件多了会增加生命周期管理成本
引用关系可能变复杂
update 调度可能变多
组件之间可能强耦合所以原则是:
text
按职责合理拆分,不是越碎越好🧠 生命周期复盘
常见生命周期顺序:
text
onLoad
↓
onEnable
↓
start
↓
update
↓
onDisable
↓
onDestroy记法:
text
onLoad:初始化自身
onEnable:进入启用状态
start:第一次 update 前
update:每帧执行
onDisable:退出启用状态
onDestroy:销毁清理工程习惯:
text
onEnable 注册事件,优先在 onDisable 取消。
不用 update,就不要写空 update。🧠 update 是否执行,看哪些条件?
一个组件的 update 能执行,通常要满足:
text
1. 组件实现了 update
2. component.enabled = true
3. node.activeInHierarchy = true
4. 对象没有被 destroy所以父节点 active=false 时,子节点虽然:
text
child.active = true但:
text
child.activeInHierarchy = false因此子节点组件不会 update。
关键点:
不是父节点 update 不调用导致子节点 update 不调用,而是子节点最终激活状态
activeInHierarchy=false,所以它不进入 update 调度。
🧠 active / enabled / destroy 复盘
这三个是不同层级:
text
active:Node 层级
enabled:Component 层级
destroy:对象生命周期终止active
ts
node.active = false;影响:
text
activeInHierarchy 变 false
组件触发 onDisable
update 停止
事件停止
渲染停止
子节点也受影响
节点仍然存在enabled
ts
component.enabled = false;影响:
text
这个组件停止工作
这个组件不 update
通常触发 onDisable
其他组件不一定受影响
Node 仍然 activedestroy
ts
node.destroy();影响:
text
对象标记为销毁
后续由引擎清理
触发 onDestroy
之后不应该继续使用💼 实际项目怎么选?
临时隐藏界面
用:
ts
panel.active = false;适合:
text
背包
商城
任务
活动页
反复打开关闭的弹窗只是关闭某个功能
用:
ts
component.enabled = false;例如:
text
头像还显示,但不旋转
界面还显示,但倒计时暂停
节点还显示,但某个脚本不执行以后不用了
用:
ts
node.destroy();适合:
text
一次性特效
确定不复用的节点
切场景清理但频繁出现的对象不建议频繁 destroy / instantiate,更适合对象池。
🔍 从 API 到引擎数据流:完整例子
假设有一个活动按钮:
text
ActivityButton
├── Sprite
├── Button
├── RedPoint
└── ActivityButtonScript你写:
ts
ActivityButton.active = false;发生的事情大概是:
text
ActivityButton.active 变 false
↓
ActivityButton.activeInHierarchy 变 false
↓
RedPoint 等子节点 activeInHierarchy 也变 false
↓
ActivityButtonScript 触发 onDisable
↓
Button 组件停止响应
↓
Sprite / RedPoint 不再显示
↓
ActivityButtonScript.update 不再执行
↓
节点仍然存在,可以 active=true 恢复你写:
ts
ActivityButton.getComponent(cc.Button).interactable = false;发生的是:
text
Button 组件变成不可交互
↓
按钮通常仍显示
↓
节点仍 active
↓
其他组件仍工作
↓
只是不能点击或进入不可交互状态你写:
ts
ActivityButton.destroy();发生的是:
text
节点被标记销毁
↓
子节点和组件也会进入销毁流程
↓
onDestroy 触发
↓
后续不应该继续使用这个节点🚀 高级开发怎么用这些知识?
初级开发看到问题:
text
按钮点不了可能只会说:
text
是不是没绑定事件?高级开发会按层排查:
text
1. Node active 是否为 true?
2. 父节点 activeInHierarchy 是否为 true?
3. Button Component enabled 是否为 true?
4. Button interactable 是否为 true?
5. 是否有遮挡节点?
6. 是否注册了点击事件?
7. 是否事件被父节点拦截?
8. 是否节点尺寸 / hit area 不对?
9. 是否脚本生命周期没走到?这就是从 API 使用者到引擎理解者的区别。
📝 阶段综合练习参考答案
题目结构:
text
Root
└── Panel
├── StartButton
│ ├── Sprite
│ ├── Button
│ └── StartButtonScript
└── TimeLabel
├── Label
└── CountDownScript当前状态:
text
Root.active = true
Panel.active = false
StartButton.active = true
TimeLabel.active = true
StartButtonScript.enabled = true
CountDownScript.enabled = true答案:
StartButton.active = true。父节点不会改掉子节点自己的 active。StartButton.activeInHierarchy = false。因为父节点 Panel.active=false。StartButtonScript.update不会执行,因为节点 activeInHierarchy=false。TimeLabel不会显示,因为它的 activeInHierarchy=false。- 如果
Panel.active = true,并且其他状态不变:- Panel.activeInHierarchy = true
- StartButton.activeInHierarchy = true
- TimeLabel.activeInHierarchy = true
- StartButton 显示
- TimeLabel 显示
- Button 可以响应
- StartButtonScript.update 恢复
- CountDownScript.update 恢复
- 相关组件可能触发 onEnable
- 如果只想让 TimeLabel 显示但倒计时不更新:
ts
this.countDownScript.enabled = false;- 如果 Panel 后续会反复打开关闭,应该用:
ts
Panel.active = false;而不是频繁 destroy / instantiate。
🎤 Stage01 面试题整理
1. Node 是什么?
Node 是场景树里的空间对象和组件容器,负责 Transform、层级、active、事件和组件挂载。Node 本身不是图片、按钮或文字。
2. Component 是什么?
Component 是挂在 Node 上的能力,比如 Sprite 负责显示,Button 负责交互,自定义脚本负责业务逻辑。
3. Local 坐标和 World 坐标有什么区别?
Local 是相对父节点的坐标,World 是结合父节点变换后最终在世界中的坐标。
4. DirtyFlag 的意义是什么?
多次修改 Transform 时不立即重复计算,而是标记为脏,在需要时统一更新,减少重复计算。
5. active 和 activeInHierarchy 有什么区别?
active 是节点自身是否想激活;activeInHierarchy 是结合父节点后最终是否处于激活状态。
6. active 和 enabled 有什么区别?
active 控制 Node 及其子树;enabled 控制某个 Component。
7. active=false 和 destroy 有什么区别?
active=false 是暂时关闭,节点仍然存在;destroy 是销毁,后续不应该继续使用。
8. 为什么空 update 也有成本?
只要实现了 update,引擎就可能加入每帧调度,即使函数为空也有调度、遍历、判断和函数调用成本。
✅ 本课总结
Stage01 你应该形成这条主线:
text
API 调用
↓
Node / Component 数据变化
↓
DirtyFlag / activeInHierarchy / enabled 状态变化
↓
生命周期触发
↓
update / 事件 / 渲染受到影响
↓
最终表现到屏幕和业务逻辑上记住最终结论:
text
Node 是结构和位置。
Transform 是位置、旋转、缩放。
Matrix 是底层变换计算。
Component 是能力。
Lifecycle 决定组件什么时候运行。
active 控制节点树。
enabled 控制组件。
destroy 结束对象生命周期。Stage01 完成。下一阶段:Stage02|Engine Loop。