外观
Lesson 002|this.node.x = 100 到底发生了什么?
Tags: #Creator2.x #Node #DirtyFlag #RendererDifficulty: ⭐☆☆☆☆
🎯 本课目标
理解一行代码从修改数据到最终显示在屏幕上的完整流程。
🧩 上节课回顾
上一课我们建立了 Creator 2.x 的双路径架构:Web 和 Native 共享业务语义,但不保证经过相同的内部模块。
今天开始,我们沿着一行代码真正走一遍。
🧠 先建立稳定流程
text
业务写入 node.x
↓
Node 本地 Transform 状态改变
↓
相关 Transform 被标记为需要更新
↓
后续阶段读取新的本地/世界变换
↓
可见组件准备或复用渲染数据
↓
Renderer 按 Camera、Material、状态和顺序组织提交
↓
GPU 在对应 Pass 中生成像素这是职责模型,不是 Creator 2.4.x 所有平台的逐行源码。Web 与 Native 的字段、Binding 和渲染后端可能不同,但“先改变状态,后续系统消费状态”是稳定理解。
💡 为什么需要 Dirty Flag?
如果每修改一次属性都立刻完成整棵子树矩阵、顶点和 GPU 提交:
- CPU 重复计算
- 大量无效开销
因此引擎使用 Dirty Flag(脏标记) 或类似失效机制:
修改时记录“旧结果不能再使用”,真正计算在需要该结果的阶段发生。
延迟更新的价值是合并重复修改,但不保证每种数据都只计算一次。代码在更新后立刻主动读取世界变换、多个 Camera/Pass 消费状态,或者中途再次修改,都可能产生额外工作。
💼 第一步:同一帧连续修改
ts
node.x = 100;
node.y = 200;
node.rotation = 45;
node.scale = 2;通常这些写入先改变同一 Node 的本地状态,后续消费者只需要最终值。稳定结论不是“World Matrix 一定只算一次、GPU 一定只画一次”,而是中间三个值不会自动产生三张屏幕画面。
💼 第二步:加入父节点
text
Panel
└── IconIcon.x 是相对 Panel 的本地坐标。Panel 的位置、旋转或缩放变化时,Icon 自己的 x 没变,但它的世界矩阵结果会变化。脏状态可能沿子树传播,因此一个父节点修改可能影响大量后代。
💼 第三步:加入渲染条件
Node 本身不直接产生 DrawCall。只有可见渲染组件、Camera、Material、Texture、Blend/Stencil、层级顺序等条件共同满足,Renderer 才组织提交。一个 Node 也可能不绘制、被多个 Camera 绘制,或者经过多个 Pass。
🧭 项目中怎么判断成本
text
只改 Transform → 看子树规模和 Transform 更新
改 SpriteFrame/尺寸 → 可能影响 RenderData、UV、批次
改 Material → 可能改变 Pass 和合批
增加 Camera/RenderTarget → 可能重复提交或绘制
屏幕覆盖变大 → DrawCall 不变也可能增加 GPU 像素成本⚠️ 常见误区
误区:
修改 node.x 图片就立刻移动。
实际上:
修改的是运行时状态;Renderer 和 GPU 在后续阶段消费状态。业务代码返回不等于屏幕像素已经更新。
🎤 面试会怎么问
- 什么是 Dirty Flag?
- 为什么引擎喜欢延迟更新?
- 为什么 World Matrix 不立即计算?
📝 今日练习与推导答案
问题:
ts
node.x = 100;
node.y = 200;
node.rotation = 45;
node.scale = 2;问题一:这四次写入会产生四帧画面吗?
不会。它们在同一段同步业务代码中更新状态,画面由后续帧阶段统一消费最终状态。
问题二:能否因此断定世界矩阵只计算一次?
不能。延迟更新允许合并,但主动读取、父子传播、多个系统消费和中途再次修改都可能改变实际次数。需要锁定版本和调用条件判断。
问题三:能否因此断定只有一个 DrawCall?
不能。DrawCall 由渲染组件、Camera、Material、Texture、RenderState、顺序和 Pass 决定,不由“修改了几次 x”直接决定。
✅ 本课总结
以后分析性能问题,先判断发生在哪一层:
业务状态 → Transform/组件数据 → Renderer → GPU
下一课: 什么是 World Matrix?为什么父节点移动,子节点也会移动?
