计算世界空间时处理“父”变换的正确方法是什么?

问题描述 投票:0回答:1

我一直致力于开发一个简单的 2D 游戏引擎,我偶然发现了一个小问题,我的层次关系几乎可以工作(只要父级没有旋转,它就可以工作),只要我将旋转添加到然而,父对象中的每个子对象都以不保持对象内聚力的方式旋转/移动,我认为问题出在我的变换组件中。

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 the number of child transforms attached to this transform.
    /// </summary>
    public int ChildCount
    {
        get { return m_Children.Count; }
    }

    /// <summary>
    ///     Gets the child transform at the specified index.
    /// </summary>
    /// <param name="childIndex">The index of the child transform to retrieve.</param>
    /// <returns>The child transform at the specified index.</returns>
    public Transform this[int childIndex]
    {
        get { return m_Children[childIndex]; }
    }

    /// <summary>
    ///     Gets or sets the local position of the transform.
    /// </summary>
    public Vector2 LocalPosition
    {
        get { return m_LocalPosition; }
        set { m_LocalPosition = value; }
    }

    /// <summary>
    ///     Gets or sets the local rotation of the transform in degrees.
    /// </summary>
    public float LocalRotation
    {
        get { return MathHelper.RadiansToDegrees(m_LocalRotation); }
        set { m_LocalRotation = MathHelper.DegreesToRadians(value); }
    }

    /// <summary>
    ///     Gets or sets the local scale of the transform.
    /// </summary>
    public Vector2 LocalScale
    {
        get { return m_LocalScale; }
        set { m_LocalScale = value; }
    }

    /// <summary>
    ///     Gets the parent transform of this transform.
    /// </summary>
    public Transform Parent
    {
        get { return m_Parent; }
    }

    /// <summary>
    ///     Gets or sets the world position of the transform.
    /// </summary>
    /// <summary>
    /// Gets or sets the world position of the transform.
    /// </summary>
    public Vector2 WorldPosition
    {
        get
        {
            if ( m_Parent != null )
            {
                Vector2 parentPosition = m_Parent.WorldPosition;
                float parentRotation = m_Parent.WorldRotation;

                // Rotate the local position by the parent's rotation
                Vector2 rotatedPosition = LocalPosition;
                float angle = MathHelper.DegreesToRadians ( parentRotation );
                float cos = (float) Math.Cos ( angle );
                float sin = (float) Math.Sin ( angle );
                rotatedPosition = new Vector2 (
                    cos * rotatedPosition.X - sin * rotatedPosition.Y,
                    sin * rotatedPosition.X + cos * rotatedPosition.Y
                );

                // Add parent's position to the rotated local position
                return parentPosition + rotatedPosition;
            }
            return LocalPosition;
        }
        set
        {
            if ( m_Parent != null )
            {
                Vector2 parentPosition = m_Parent.WorldPosition;
                float parentRotation = m_Parent.WorldRotation;

                // Calculate the new local position by taking into account parent's rotation
                Vector2 delta = value - parentPosition;
                float angle = MathHelper.DegreesToRadians ( -parentRotation );
                float cos = (float) Math.Cos ( angle );
                float sin = (float) Math.Sin ( angle );
                LocalPosition = new Vector2 (
                    cos * delta.X - sin * delta.Y,
                    sin * delta.X + cos * delta.Y
                );
            }
            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); }
    }

    /// <summary>
    ///     Gets or sets the world scale of the transform.
    /// </summary>
    public Vector2 WorldScale
    {
        get { return m_LocalScale * (m_Parent?.WorldScale ?? Vector2.One); }
        set
        {
            m_LocalScale = value / (m_Parent?.WorldScale ?? Vector2.One);

            if (float.IsNaN(m_LocalScale.X))
            {
                m_LocalScale.X = 0;
            }

            if (float.IsNaN(m_LocalScale.Y))
            {
                m_LocalScale.Y = 0;
            }
        }
    }

    /// <summary>
    ///     Gets the list of child transforms attached to this transform.
    /// </summary>
    internal List<Transform> Children
    {
        get { return m_Children; }
    }

    #endregion

    #region Methods

    /// <summary>
    ///     Clears all child transforms attached to this transform.
    /// </summary>
    public void ClearChildren()
    {
        for (int i = m_Children.Count - 1; i >= 0; i--)
        {
            Transform child = m_Children[i];
            child.GameObject.Destroy();
            m_Children.RemoveAt(i);
        }
    }

    /// <summary>
    ///     Returns an enumerator that iterates through the child transforms.
    /// </summary>
    /// <returns>An enumerator for the child transforms.</returns>
    public IEnumerator<Transform> GetEnumerator()
    {
        return m_Children.GetEnumerator();
    }

    /// <summary>
    ///     Sets the parent of this transform.
    /// </summary>
    /// <param name="parent">The new parent transform.</param>
    /// <param name="worldSpaceStays">Whether to maintain world space position, rotation, and scale.</param>
    public void SetParent(Transform parent, bool worldSpaceStays)
    {
        if (worldSpaceStays)
        {
            Vector2 position = WorldPosition;
            float rotation = WorldRotation;
            Vector2 scale = WorldScale;

            m_Parent = parent;
            parent?.m_Children.Add(this);

            if (parent != null)
            {
                if (Scene.IsRegistered(GameObject))
                {
                    Scene.UnRegister(GameObject);
                }
            }
            else
            {
                if (!Scene.IsRegistered(GameObject))
                {
                    Scene.Register(GameObject);
                }
            }

            WorldPosition = position;
            WorldRotation = rotation;
            WorldScale = scale;
        }
        else
        {
            m_Parent = parent;
            parent?.m_Children.Add(this);

            if (parent != null)
            {
                if (Scene.IsRegistered(GameObject))
                {
                    Scene.UnRegister(GameObject);
                }
            }
            else
            {
                if (!Scene.IsRegistered(GameObject))
                {
                    Scene.Register(GameObject);
                }
            }
        }
    }

    /// <summary>
    ///     Returns an enumerator that iterates through the child transforms.
    /// </summary>
    /// <returns>An enumerator for the child transforms.</returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}
c# transform game-engine
1个回答
0
投票

您应该将 Transform 转换为矩阵。例如 System.Numerics.Matrix3x2,但还有很多替代方案。矩阵具有允许乘法将它们组合起来的巨大优势。因此提供了一种组合多个变换的简单方法。所以你应该有这样的属性:

public Matrix3x2 Local => 
    Matrix3x2.CreateScale(m_LocalScale) *
    Matrix3x2.CreateRotation(m_LocalRotation)*
    Matrix3x2.CreateTranslation(m_LocalPosition );

public Matrix3x2  Global => (m_Parent?.Global ?? Matrix3x2.Identity)  * Local;

请注意,我可能搞乱了乘法顺序。我不记得 system.Numerics 是否遵循常规顺序。如果不起作用,请尝试颠倒顺序。

这也应该避免计算世界位置等的所有数学。您应该能够直接使用矩阵来渲染事物。如果你想获得世界位置,只需将零向量与全局矩阵相乘即可。

我还会考虑添加一个中心向量来指定要缩放的点。还有一个 Angle 结构,以避免对弧度/度数产生任何混淆。

© www.soinside.com 2019 - 2024. All rights reserved.