# TransformComponent **命名空间**: `XCEngine::Components` **类型**: `class` **头文件**: `XCEngine/Components/TransformComponent.h` **描述**: `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) ## 相关文档 - [当前模块](../Components.md) - [GameObject](../GameObject/GameObject.md) - [Component](../Component/Component.md) - [API 总索引](../../../main.md)