Skip to content

Lesson 004|Transform 为什么要分 position、rotation、scale?

Tags: #Creator2.x #Node #Transform #Matrix #Scale #RotationDifficulty: ⭐⭐☆☆☆


🎯 本课目标

  • 理解 Transform 的含义
  • 理解 position、rotation、scale 各自负责什么
  • 理解为什么引擎对开发者暴露 Transform,但底层使用 Matrix
  • 理解父节点 scale / rotation 为什么会影响子节点
  • 为后续学习 Node、Renderer、顶点更新和性能优化打基础

🧩 上节课回顾

上一课我们讲了:

text
Local 是相对于父节点。
World 是最终显示到世界中的位置。
Matrix 是节点变换规则包。
父节点变了,子节点的 World Matrix 也会受影响。

今天我们继续拆 Matrix 里面最核心的内容:

text
position
rotation
scale

它们合起来就是:

Transform


💡 Transform 是什么?

在游戏引擎里,Transform 可以理解成:

一个节点在场景中如何摆放。

比如一个按钮:

text
放在哪里?
旋转了多少度?
放大还是缩小?

对应的就是:

text
position:位置
rotation:旋转
scale:缩放

所以可以简单理解为:

text
Transform = position + rotation + scale

Creator 中还会涉及 anchor、skew、size 等概念,但这一课先抓主线。


🧠 为什么不直接存 worldX / worldY?

你可能会想:

既然最终显示的是世界坐标,那每个节点直接保存 worldX、worldY 不就好了?

问题是节点不只有位置,还有:

text
rotation
scale
父子关系
anchor

例如:

text
Panel.x = 100
Panel.scale = 2
Button.x = 30

Button 的最终位置不是:

text
100 + 30 = 130

而是:

text
100 + 30 * 2 = 160

因为父节点缩放了,子节点相对于父节点的偏移也会被缩放。

如果父节点再旋转,子节点的方向也会跟着变化。

所以引擎不能只保存简单的 worldX / worldY,而需要保存完整的变换规则。

这就是 Transform 和 Matrix 存在的原因。


🧠 position:节点在哪里

position 表示节点的位置。

例如:

ts
node.x = 100;
node.y = 50;

表示:

text
节点相对于父节点偏移 x = 100, y = 50

注意:

node.x 通常是 Local 坐标,不是屏幕坐标。

在没有父节点变换影响时,它看起来像屏幕坐标;一旦父节点移动、缩放、旋转,它就不再等同于屏幕坐标。


🧠 scale:节点有多大

scale 表示缩放。

例如:

ts
node.scale = 2;

表示这个节点相对于原始大小放大 2 倍。

如果一个 Sprite 原本宽 100,高 100:

text
scale = 1    → 显示 100 × 100
scale = 2    → 显示 200 × 200
scale = 0.5  → 显示 50 × 50

但是 scale 不只影响自己。

父节点的 scale 也会影响子节点。

例如:

text
Panel.x = 100
Panel.scale = 2
Button.x = 30

Button 相对于 Panel 的偏移会被放大:

text
Button.worldX = Panel.x + Button.x * Panel.scale
              = 100 + 30 * 2
              = 160

关键点:

父节点的位置决定坐标系原点,父节点的 scale 会影响子节点的 local 偏移。


🧠 rotation:节点朝向

rotation 表示旋转。

例如:

ts
node.rotation = 45;

表示节点旋转 45 度。

在父子节点关系中,父节点旋转会带动子节点整体旋转。

例如:

text
Panel.rotation = 90
Button.x = 30

Button 原本在 Panel 的右边。

当 Panel 旋转 90 度后,Button 的 local 坐标没变,但它在世界坐标中的方向变了。

这就是为什么只用 x、y 不够。


🔍 为什么 Transform 要拆成 position、rotation、scale?

因为开发者需要分别控制它们。

移动:

ts
node.x += 10;

缩放:

ts
node.scale = 1.2;

旋转:

ts
node.rotation += 10;

如果引擎只暴露 Matrix 给开发者,那么每次移动、旋转、缩放都要自己计算矩阵,这对业务开发非常不友好。

所以引擎设计成:

text
对开发者:暴露 position、rotation、scale,直观易用
对引擎底层:合成为 Matrix,统一高效计算

这就是设计上的分层。


🔍 从开发者视角到引擎视角

你平时写:

ts
node.x = 100;
node.rotation = 45;
node.scale = 2;

开发者视角:

text
位置改了
角度改了
大小改了

引擎视角:

text
Local Transform 变了
Local Matrix 需要更新
World Matrix 可能需要更新
子节点 World Matrix 也可能受影响
Renderer 需要用新的矩阵更新顶点

普通开发看到 API。

高级开发看到 API 背后的数据流。


💼 工作中的真实案例

案例一:不要在复杂父节点上频繁做 scale 动画

假设一个复杂活动面板:

text
ActivityPanel
├── Background
├── Title
├── Button
├── List
├── Spine
└── Effect

如果每帧执行:

ts
this.panel.scale += 0.01;

这不只是面板自己变大。

它可能导致:

  • Panel 的 World Matrix 更新
  • 所有子节点的 World Matrix 更新
  • 大量顶点重新计算
  • UI 合批可能受影响

所以复杂 UI 的缩放动画要谨慎。


案例二:旋转父节点会影响所有子节点

例如转盘活动:

ts
this.wheelRoot.rotation += 5;

所有奖品图标、文字、特效都会跟着旋转。

这很方便,但也意味着整棵子树都跟着变换。

节点越多,更新成本越高。


案例三:scale = 0 不等于隐藏

有些人为了隐藏节点,会写:

ts
node.scale = 0;

这不推荐。

更常见的方式是:

ts
node.active = false;

或者:

ts
node.opacity = 0;

具体选择要看需求。

scale = 0 只是把节点缩小为 0,它仍然可能参与部分逻辑、布局或更新。

active = false 会让节点及其子节点进入非激活状态,但频繁切换 active 本身也有成本。


⚠️ 常见误区

误区一:position、rotation、scale 是完全独立的

从 API 角度看,它们是独立的。

但最终渲染时,它们会合成一个 Matrix,一起影响显示结果。


误区二:scale 只影响节点大小

scale 不只影响节点自己,也会影响子节点的位置和大小。

父节点 scale 变化后,子节点最终显示也会变化。


误区三:rotation 只是自己旋转

父节点 rotation 会影响整棵子树。

子节点自己的 local position 不变,但 world position 会变。


🎤 面试会怎么问

问题一:Transform 是什么?

参考回答:

Transform 表示一个节点的空间变换,通常包括 position、rotation、scale。它描述节点相对于父节点如何摆放。


问题二:为什么引擎内部要使用 Matrix?

参考回答:

因为 position、rotation、scale 以及父子节点变换最终需要统一计算。Matrix 可以把这些变换组合成一个统一的数据结构,方便计算世界变换和渲染顶点。


问题三:为什么父节点 scale 会影响子节点?

参考回答:

因为子节点的 World Matrix 由父节点的 World Matrix 和自身 Local Matrix 合成。父节点 scale 改变后,子节点的世界变换也会随之改变。


📝 今日练习

假设有下面结构:

text
Root
└── Panel
    └── Button

属性如下:

text
Root.x = 0
Panel.x = 100
Panel.scale = 2
Button.x = 30

先不考虑旋转。

回答:

  1. 如果 Panel.scale = 1,Button.worldX 是多少?
  2. 如果 Panel.scale = 2,Button.worldX 大概是多少?
  3. Button 自己的 x 有没有变化?
  4. 为什么 scale 会影响 Button 的 worldX?

📝 今日练习参考答案

  1. 如果 Panel.scale = 1:
text
Button.worldX = Root.x + Panel.x + Button.x
              = 0 + 100 + 30
              = 130
  1. 如果 Panel.scale = 2:
text
Button.worldX = Root.x + Panel.x + Button.x * Panel.scale
              = 0 + 100 + 30 * 2
              = 160

注意不是:

text
130 * 2 = 260

因为 Panel 自己的位置 Panel.x = 100 不会被自己的 scale 放大。

被放大的是:

text
Button 相对于 Panel 的 local 偏移 30
  1. Button 自己的 x 没有变化,仍然是:
text
Button.x = 30
  1. Button 的 worldX 变化,是因为 Button 的世界变换受父节点 Panel 的 Matrix 影响。父节点 scale 会放大子节点的 local 偏移。

🚀 能力成长

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

  • 能解释 Transform 的基本含义
  • 能区分 position、rotation、scale 的职责
  • 能解释为什么引擎对开发者暴露 Transform,但底层使用 Matrix
  • 能解释为什么父节点 scale / rotation 会影响子节点
  • 能初步分析复杂父节点动画带来的性能影响

✅ 本课总结

记住五句话:

text
Transform = position + rotation + scale。
position 决定节点相对父节点的位置。
scale 会影响节点和子节点的偏移与大小。
rotation 会影响节点和子节点的方向。
开发者操作 Transform,引擎底层合成为 Matrix。

下一课: Node 到底是什么?为什么它是 Creator 的核心?