6.0 KiB
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) 的行为容易被误解。按当前实现,它不是“从当前节点的子树开始找”,而是:
- 通过宿主
GameObject拿到所属Scene - 从场景所有 root game objects 开始递归搜索
- 找到第一个名称匹配的
GameObject后返回其Transform
因此,它更像一个场景级按名称查找的快捷入口,而不是严格的局部子树查询 API。
序列化语义
Serialize() / Deserialize() 当前只处理局部变换:
positionrotationscale
不会直接写出父子关系,也不会直接写出世界矩阵缓存。世界空间数据会在反序列化后通过 SetDirty() 延迟重建。
这和商业引擎常见做法一致:持久化局部值,把世界值当运行时派生结果。
线程语义
- 当前实现没有内部加锁。
m_dirty、父子列表和缓存矩阵都按单线程访问模型设计。- 默认应在主线程或明确受控的场景更新线程上读写。
当前实现限制
SetDirty()会递归标记所有子节点,这是正确方向,但如果层级很深,批量修改会带来明显传播成本。SetSiblingIndex()、SetAsFirstSibling()、SetAsLastSibling()当前只修改m_siblingIndex数值,没有真正重排父节点m_children顺序。DetachChildren()只改TransformComponent自己的父子指针,不会同步处理GameObject层级与Scene根节点列表。Find()是场景级名称搜索,不是局部子树搜索。
这些限制非常值得在编辑器或脚本层显式规避。
推荐使用方式
- 日常层级重组优先用
GameObject::SetParent()。 - 把
TransformComponent视为内建基础设施,不要尝试把它当普通组件那样增删。 - 需要保存/加载时只关心局部值,不要手动持久化世界矩阵。
- 使用
Find()时明确它是场景级名称匹配,不要把它当路径查询接口。
相关方法
- Constructor
- GetLocalPosition
- SetLocalPosition
- GetLocalRotation
- SetLocalRotation
- GetLocalScale
- SetLocalScale
- GetPosition
- SetPosition
- GetRotation
- SetRotation
- GetScale
- SetScale
- GetLocalToWorldMatrix
- GetWorldToLocalMatrix
- SetParent
- Find
- DetachChildren
- SetDirty
- UpdateWorldTransform
- LookAt
- Rotate
- Translate
- TransformPoint
- InverseTransformPoint
- TransformDirection
- InverseTransformDirection
- Serialize
- Deserialize