外观
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
↓
渲染组件或 Renderer 消费新的 Transform / RenderData
↓
Batch / DrawCall
↓
GPU 显示到屏幕所以:
改
node.x不是直接操作屏幕,也不是直接操作 GPU,而是先改 Node 的 Transform 数据。
这是一条稳定的职责模型,不代表 Web 与 Native 都经过相同类和函数,也不保证每次修改都会重建顶点或只产生一个 DrawCall。实际成本还取决于父子树、组件类型、Camera、Material、RenderState 和 Pass。
🧠 为什么有 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:结合父节点后的世界坐标结果
View / Clip / Screen:经过 Camera、投影和视口后的观察、裁剪与屏幕结果例如:
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 是结合父节点变换后的世界坐标;它还要经过 Camera、投影和视口,才会成为 Screen 坐标。
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。
