2026-03-26 16:45:24 +08:00
|
|
|
|
# TransformComponent
|
|
|
|
|
|
|
|
|
|
|
|
**命名空间**: `XCEngine::Components`
|
|
|
|
|
|
|
|
|
|
|
|
**类型**: `class`
|
|
|
|
|
|
|
|
|
|
|
|
**头文件**: `XCEngine/Components/TransformComponent.h`
|
|
|
|
|
|
|
2026-03-27 16:15:00 +08:00
|
|
|
|
**描述**: `GameObject` 的内建变换组件,负责局部/世界空间变换、层级矩阵计算和常见的空间操作。
|
|
|
|
|
|
|
|
|
|
|
|
## 角色概述
|
|
|
|
|
|
|
|
|
|
|
|
`TransformComponent` 是当前对象树里最基础、也是最特殊的组件。它既继承自 `Component`,又不属于普通“可随意添加和删除”的组件集合:
|
|
|
|
|
|
|
|
|
|
|
|
- 每个 `GameObject` 构造时都会创建一个 `TransformComponent`。
|
|
|
|
|
|
- 普通组件通过 `Component::transform()` 获取它。
|
|
|
|
|
|
- 场景层级和空间层级的大部分语义都围绕它展开。
|
|
|
|
|
|
|
|
|
|
|
|
如果把整个 `Components` 模块类比成 Unity 风格对象系统,那么 `TransformComponent` 对应的就是最接近 Unity `Transform` 的那一层基础设施。
|
|
|
|
|
|
|
|
|
|
|
|
## 局部与世界变换
|
|
|
|
|
|
|
|
|
|
|
|
类型内部同时维护:
|
|
|
|
|
|
|
|
|
|
|
|
- 局部数据:`m_localPosition`、`m_localRotation`、`m_localScale`
|
|
|
|
|
|
- 派生缓存:`m_localToWorldMatrix`、`m_worldPosition`、`m_worldRotation`、`m_worldScale`
|
|
|
|
|
|
- 脏标记:`m_dirty`
|
|
|
|
|
|
|
|
|
|
|
|
当前实现使用懒更新模式:
|
|
|
|
|
|
|
|
|
|
|
|
- 修改局部位置、旋转或缩放时,调用 `SetDirty()`
|
|
|
|
|
|
- 真正读取世界矩阵、世界位置、世界旋转或世界缩放时,才通过 `UpdateWorldTransform()` 重新计算
|
|
|
|
|
|
|
|
|
|
|
|
这是一种常见的商业引擎做法,优点是多个 setter 可以先累积,再在真正需要世界空间数据时统一计算;代价是层级脏标记传播一定要正确,否则很容易出现缓存过期问题。
|
|
|
|
|
|
|
|
|
|
|
|
## 层级关系
|
|
|
|
|
|
|
|
|
|
|
|
`TransformComponent` 自己维护一套父子变换树:
|
|
|
|
|
|
|
|
|
|
|
|
- `GetParent()`
|
|
|
|
|
|
- `SetParent(TransformComponent*, bool worldPositionStays = true)`
|
|
|
|
|
|
- `GetChild()`
|
|
|
|
|
|
- `GetChildCount()`
|
|
|
|
|
|
- `DetachChildren()`
|
|
|
|
|
|
|
|
|
|
|
|
但要注意,变换树和 `GameObject` 的对象树虽然通常同步,却不是完全同一个概念。
|
|
|
|
|
|
|
|
|
|
|
|
### 推荐做法
|
|
|
|
|
|
|
|
|
|
|
|
当你操作真实场景对象关系时,优先使用 `GameObject::SetParent()`。
|
|
|
|
|
|
|
|
|
|
|
|
原因是 `GameObject::SetParent()` 会同时处理:
|
|
|
|
|
|
|
|
|
|
|
|
- `GameObject` 子列表
|
|
|
|
|
|
- 场景根节点列表
|
|
|
|
|
|
- 激活层级变化传播
|
|
|
|
|
|
- 对应 `Transform` 的父子关系
|
|
|
|
|
|
|
|
|
|
|
|
而直接调用 `TransformComponent::SetParent()` 只会改变变换树,不会完整维护场景对象树的其他语义。
|
|
|
|
|
|
|
|
|
|
|
|
## 空间操作
|
|
|
|
|
|
|
|
|
|
|
|
`TransformComponent` 提供了当前最常用的一组空间 API:
|
|
|
|
|
|
|
|
|
|
|
|
- 世界/局部位置、旋转、缩放读写
|
|
|
|
|
|
- `GetForward()`、`GetRight()`、`GetUp()`
|
|
|
|
|
|
- `LookAt()`
|
|
|
|
|
|
- `Rotate()`、`Translate()`
|
|
|
|
|
|
- `TransformPoint()` / `InverseTransformPoint()`
|
|
|
|
|
|
- `TransformDirection()` / `InverseTransformDirection()`
|
|
|
|
|
|
|
|
|
|
|
|
从实现看,这些方法都是直接基于 `Quaternion`、`Matrix4x4` 和当前缓存矩阵完成的轻量封装,没有额外的物理、动画或约束系统参与。
|
|
|
|
|
|
|
|
|
|
|
|
## 查找语义
|
|
|
|
|
|
|
|
|
|
|
|
`Find(const std::string& name)` 的行为容易被误解。按当前实现,它不是“从当前节点的子树开始找”,而是:
|
|
|
|
|
|
|
|
|
|
|
|
1. 通过宿主 `GameObject` 拿到所属 `Scene`
|
|
|
|
|
|
2. 从场景所有 root game objects 开始递归搜索
|
|
|
|
|
|
3. 找到第一个名称匹配的 `GameObject` 后返回其 `Transform`
|
|
|
|
|
|
|
|
|
|
|
|
因此,它更像一个场景级按名称查找的快捷入口,而不是严格的局部子树查询 API。
|
|
|
|
|
|
|
|
|
|
|
|
## 序列化语义
|
|
|
|
|
|
|
|
|
|
|
|
`Serialize()` / `Deserialize()` 当前只处理局部变换:
|
|
|
|
|
|
|
|
|
|
|
|
- `position`
|
|
|
|
|
|
- `rotation`
|
|
|
|
|
|
- `scale`
|
|
|
|
|
|
|
|
|
|
|
|
不会直接写出父子关系,也不会直接写出世界矩阵缓存。世界空间数据会在反序列化后通过 `SetDirty()` 延迟重建。
|
|
|
|
|
|
|
|
|
|
|
|
这和商业引擎常见做法一致:持久化局部值,把世界值当运行时派生结果。
|
|
|
|
|
|
|
|
|
|
|
|
## 线程语义
|
|
|
|
|
|
|
|
|
|
|
|
- 当前实现没有内部加锁。
|
|
|
|
|
|
- `m_dirty`、父子列表和缓存矩阵都按单线程访问模型设计。
|
|
|
|
|
|
- 默认应在主线程或明确受控的场景更新线程上读写。
|
|
|
|
|
|
|
|
|
|
|
|
## 当前实现限制
|
|
|
|
|
|
|
|
|
|
|
|
- `SetDirty()` 会递归标记所有子节点,这是正确方向,但如果层级很深,批量修改会带来明显传播成本。
|
|
|
|
|
|
- `SetSiblingIndex()`、`SetAsFirstSibling()`、`SetAsLastSibling()` 当前只修改 `m_siblingIndex` 数值,没有真正重排父节点 `m_children` 顺序。
|
|
|
|
|
|
- `DetachChildren()` 只改 `TransformComponent` 自己的父子指针,不会同步处理 `GameObject` 层级与 `Scene` 根节点列表。
|
|
|
|
|
|
- `Find()` 是场景级名称搜索,不是局部子树搜索。
|
|
|
|
|
|
|
|
|
|
|
|
这些限制非常值得在编辑器或脚本层显式规避。
|
|
|
|
|
|
|
|
|
|
|
|
## 推荐使用方式
|
|
|
|
|
|
|
|
|
|
|
|
1. 日常层级重组优先用 `GameObject::SetParent()`。
|
|
|
|
|
|
2. 把 `TransformComponent` 视为内建基础设施,不要尝试把它当普通组件那样增删。
|
|
|
|
|
|
3. 需要保存/加载时只关心局部值,不要手动持久化世界矩阵。
|
|
|
|
|
|
4. 使用 `Find()` 时明确它是场景级名称匹配,不要把它当路径查询接口。
|
|
|
|
|
|
|
|
|
|
|
|
## 相关方法
|
|
|
|
|
|
|
|
|
|
|
|
- [Constructor](Constructor.md)
|
|
|
|
|
|
- [GetLocalPosition](GetLocalPosition.md)
|
|
|
|
|
|
- [SetLocalPosition](SetLocalPosition.md)
|
|
|
|
|
|
- [GetLocalRotation](GetLocalRotation.md)
|
|
|
|
|
|
- [SetLocalRotation](SetLocalRotation.md)
|
|
|
|
|
|
- [GetLocalScale](GetLocalScale.md)
|
|
|
|
|
|
- [SetLocalScale](SetLocalScale.md)
|
|
|
|
|
|
- [GetPosition](GetPosition.md)
|
|
|
|
|
|
- [SetPosition](SetPosition.md)
|
|
|
|
|
|
- [GetRotation](GetRotation.md)
|
|
|
|
|
|
- [SetRotation](SetRotation.md)
|
|
|
|
|
|
- [GetScale](GetScale.md)
|
|
|
|
|
|
- [SetScale](SetScale.md)
|
|
|
|
|
|
- [GetLocalToWorldMatrix](GetLocalToWorldMatrix.md)
|
|
|
|
|
|
- [GetWorldToLocalMatrix](GetWorldToLocalMatrix.md)
|
|
|
|
|
|
- [SetParent](SetParent.md)
|
|
|
|
|
|
- [Find](Find.md)
|
|
|
|
|
|
- [DetachChildren](DetachChildren.md)
|
|
|
|
|
|
- [SetDirty](SetDirty.md)
|
|
|
|
|
|
- [UpdateWorldTransform](UpdateWorldTransform.md)
|
|
|
|
|
|
- [LookAt](LookAt.md)
|
|
|
|
|
|
- [Rotate](Rotate.md)
|
|
|
|
|
|
- [Translate](Translate.md)
|
|
|
|
|
|
- [TransformPoint](TransformPoint.md)
|
|
|
|
|
|
- [InverseTransformPoint](InverseTransformPoint.md)
|
|
|
|
|
|
- [TransformDirection](TransformDirection.md)
|
|
|
|
|
|
- [InverseTransformDirection](InverseTransformDirection.md)
|
|
|
|
|
|
- [Serialize](Serialize.md)
|
|
|
|
|
|
- [Deserialize](Deserialize.md)
|
|
|
|
|
|
|
|
|
|
|
|
## 相关指南
|
|
|
|
|
|
|
|
|
|
|
|
- [GameObject-Component Lifecycle And Serialization](../../../_guides/Components/GameObject-Component-Lifecycle-And-Serialization.md)
|
2026-03-26 16:45:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 相关文档
|
|
|
|
|
|
|
2026-03-27 16:15:00 +08:00
|
|
|
|
- [当前模块](../Components.md)
|
|
|
|
|
|
- [GameObject](../GameObject/GameObject.md)
|
|
|
|
|
|
- [Component](../Component/Component.md)
|
|
|
|
|
|
- [API 总索引](../../../main.md)
|