计算世界空间时如何正确处理父Transform变换?
2D游戏引擎Transform组件层级变换问题修复
问题根源分析
你的Transform组件存在两个核心问题,导致父对象旋转/缩放后子对象无法保持正确的聚合关系:
1. 世界旋转计算逻辑完全颠倒
WorldRotation的getter和setter逻辑搞反了,世界旋转应该是局部旋转叠加父对象的世界旋转,而非减法运算。当前代码会导致子对象旋转方向与预期相反,层级旋转叠加完全错误。
2. 世界位置计算未考虑父对象的缩放
计算子对象世界位置时,仅对局部位置做了旋转处理,但未应用父对象的缩放变换,导致父对象缩放后,子对象的位置偏移无法同步缩放,直接破坏了对象间的聚合性。
修复后的Transform核心代码
以下是修正后的关键属性实现(其余代码保持不变):
using OpenTK.Mathematics; using System.Collections; namespace Aurora; /// <summary> /// Represents the transformation of a game object in the scene. /// </summary> public class Transform : Component, IEnumerable<Transform> { #region Fields [SerializeField] private readonly List<Transform> m_Children = new(); [SerializeField] private Vector2 m_LocalPosition = Vector2.Zero; [SerializeField] private float m_LocalRotation; [SerializeField] private Vector2 m_LocalScale = Vector2.One; [SerializeField] private Transform m_Parent; #endregion #region Properties // 其他属性保持不变,仅修改以下核心属性 /// <summary> /// Gets or sets the world position of the transform. /// </summary> public Vector2 WorldPosition { get { if (m_Parent != null) { Vector2 parentPosition = m_Parent.WorldPosition; Vector2 parentScale = m_Parent.WorldScale; float parentRotation = m_Parent.WorldRotation; // 标准变换顺序:缩放 → 旋转 → 平移 Vector2 scaledLocalPos = LocalPosition * parentScale; float angle = MathHelper.DegreesToRadians(parentRotation); float cos = (float)Math.Cos(angle); float sin = (float)Math.Sin(angle); Vector2 rotatedPosition = new Vector2( cos * scaledLocalPos.X - sin * scaledLocalPos.Y, sin * scaledLocalPos.X + cos * scaledLocalPos.Y ); return parentPosition + rotatedPosition; } return LocalPosition; } set { if (m_Parent != null) { Vector2 parentPosition = m_Parent.WorldPosition; Vector2 parentScale = m_Parent.WorldScale; float parentRotation = m_Parent.WorldRotation; // 反向推导局部位置:逆平移 → 逆旋转 → 逆缩放 Vector2 delta = value - parentPosition; float angle = MathHelper.DegreesToRadians(-parentRotation); float cos = (float)Math.Cos(angle); float sin = (float)Math.Sin(angle); Vector2 rotatedDelta = new Vector2( cos * delta.X - sin * delta.Y, sin * delta.X + cos * delta.Y ); // 避免除以0导致NaN LocalPosition = new Vector2( parentScale.X != 0 ? rotatedDelta.X / parentScale.X : 0, parentScale.Y != 0 ? rotatedDelta.Y / parentScale.Y : 0 ); } else { LocalPosition = value; } } } /// <summary> /// Gets or sets the world rotation of the transform in degrees. /// </summary> public float WorldRotation { get { return LocalRotation + (m_Parent?.WorldRotation ?? 0); } set { LocalRotation = value - (m_Parent?.WorldRotation ?? 0); } } // 其他属性保持不变 #endregion // 其他方法保持不变 }
额外说明
- 图形学中标准的变换顺序为缩放 → 旋转 → 平移,遵循此顺序才能保证子对象跟随父对象的所有变换保持正确空间关系。
- 在
WorldPosition的setter中增加了缩放判零处理,避免出现NaN值导致的异常。
内容的提问来源于stack exchange,提问作者RWolfe
相关产品推荐
相关产品推荐

