0

我正在使用 Xna 4,做一个游戏,我有一个游戏对象(宇宙飞船),它在 Y 轴 = 0 平面上的 3D 世界中移动.. 又名 2.5D..

到目前为止,我使用非常复杂的角度计算来创建 2 个向量之间的旋转,但该算法缺乏考虑对象已经旋转的能力。所以结果变得很有趣..因此我希望有人可以向我展示一种使用矩阵和矢量数学的智能且易于实现的方法,随着时间的推移进行这样的旋转。

我在之前的搜索中注意到,人们在他们的对象类中有以下变量: - 位置向量 3 - 右向量 3 - 上向量 3 - 旋转矩阵 - 变换矩阵矩阵 - 速度向量 3 - 等等。

我经常问自己,为什么对于一个简单的当前位置需要有这么多变量..或者我可能不理解..无论如何..

我目前有位置、旋转和变换矩阵,想知道我还需要什么以及如何计算它,然后你将如何随着时间的推移实现旋转。

我的右键单击移动命令 trig 调用的方法在单击发生位置的 Y = 0 平面上发送 vector3 位置。

public void MoveCommand(Vector3 pos){ }

我对此进行了测试,给出的“pos”是准确的。任何帮助将不胜感激..

4

3 回答 3

0

您应该根据您想要的旋转检查 Matrix.CreateRotationX Y 或 Z。X、Y 或 Z 是旋转轴,如果选择 Y,您将看到“2D”旋转(偏航),因为这是您用作深度的轴。如果您选择 X 或 Z 轴,您将看到“3D”旋转(俯仰和滚动)

代码应如下所示:

WorldMatrix = Rotations * Translation

其中 Rotations = Matrix.CreateRotationX(angleRadians) 和 Translation = Matrix.CreateTranslation(Position);

世界矩阵是影响模型的矩阵,视图和投影取决于相机

现在,如果您想知道向量之间的角度,您应该检查点积或 atan2 函数,因为您在 2D 中

于 2013-02-19T21:01:19.097 回答
0
Vector3 Position;
float Rotation;

Matrix World
{
    get
    {
        return Matrix.CreateRotationZ(Rotation) * Matrix.CreateTranslation(Position);
    }
}

public void RotateInstantly(Vector3 position)
{
    Rotation = Math.Atan2(Position.Y - position.Y, Position.x - position.x);
}

public void RotateIncremently(Vector3 position, float maxStep)
{
    float targetRotation = Math.Atan2(Position.Y - position.Y, Position.x - position.x);
    float diff = targetRotation - Rotation;

    if (Math.Abs(diff) > maxStep)
    {
        if (targetRotation > Rotation)
            Rotation += maxStep;
        else
            Rotation -= maxStep;
    }
    else
        Rotation = targetRotation;
}

您可以像这样使用 RotateIncremently:* float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;

浮动 maxRotationVelocity = Math.TwoPi; //2* Pi 是一圈。

RotateIncremently(target.Position, maxRotationVelocity * dt);

于 2013-02-20T11:40:03.037 回答
0

感谢 Stig-Rune Skansgård 的回复(如果你是丹麦语hi5),我修复了我的旧角度计算并让它在每种情况下都能正常工作。所以我想我会用适合我的解决方案来回答我自己的问题,这样未来访问者可以从中受益。这是一个非常大的 Ship 类的片段 + 一个计算角度的辅助方法:

public static float CalculateAngleFromVectors(Vector3 v1, Vector3 v2) {
        float x = 0;
        float X = v2.X - v1.X,
              Z = v2.Z - v1.Z;
        if (Z == 0) {
            x = (X < 0 ? 90 : 270);
        } else if (X == 0) {
            x = (Z < 0 ? 180 : -180);
        } else {
            float temp = MathHelper.ToDegrees((float)Math.Atan(X / Z));
            if (X < 0) {
                x = (Z < 0 ? Math.Abs(temp) : 180 - Math.Abs(temp));
            } else {
                x = (Z < 0 ? 360 - Math.Abs(temp) : Math.Abs(temp) + 180);
            }
        }
        return x;
    }

此方法为您提供浮动角度(以度为单位)以旋转您的船(从标准 0 度起点)..

要使用它,只需在您的类中创建一些更新/动画方法,执行如下操作:

float desiredRotation = 0, currentRotation = 0, totalElapsed = 0, timePerFrame = 0.05f;
        if (desiredRotation != 0) {
            totalElapsed += elapsed;
            if (totalElapsed > timePerFrame) {
                if (isRotationComplete()) {
                    rotX += MathHelper.ToRadians(desiredRotation);
                    currentRotation = desiredRotation = 0;
                } else if (desiredRotation > currentRotation) {
                    currentRotation += shipTurnSpeed;
                } else if (desiredRotation < currentRotation) {
                    currentRotation -= shipTurnSpeed;
                }
                totalElapsed -= timePerFrame;
            }
        }

编辑:和完成检查:

private bool isRotationComplete() {
        bool b = false;
        if (desiredRotation > currentRotation && currentRotation + shipTurnSpeed > desiredRotation) {
            b = true;
        } else if (desiredRotation < currentRotation && currentRotation - shipTurnSpeed < desiredRotation) {
            b = true;
        }
        return b;
    }

本质上,它的作用是始终检查 DesiredRotation 是否大于 0.. 如果是,则意味着玩家已发出旋转命令(或 AI).. 在我的示例中,CurrentRotation 是 ofc 多少自上次给出旋转命令以来已旋转,并在旋转完成后设置为 0。你应该有一个旋转矩阵,它使用不同的变量来显示旋转。我的是这样的:

public float rotX { get; set; }
    public float rotY { get; set; }
    public Vector3 position { get; set; }
    public Matrix Transform {
        get {
            return (Matrix.Identity *
                    Matrix.CreateScale(scale) *
                    Matrix.CreateRotationY(MathHelper.Pi) *
                    Rotation *
                    Matrix.CreateTranslation(position));
        }
    }
    public float ShipCurRotation { get { return (rotX + MathHelper.ToRadians(currentRotation)); } }
    public Matrix Rotation { get { return (Matrix.CreateRotationY(ShipCurRotation) * Matrix.CreateRotationX(rotY)); } }

rotX 变量在旋转完成时在我的动画中设置,也在 Init 处设置。这是我如何使用我的第一个代码片段为我生成的旋转角度:

public void MoveToPosition(Vector3 pos) {
        desiredRotation = (CalculateAngleFromVectors(position, pos) - MathHelper.ToDegrees(rotX));
        isMoving = true;
    }

..这使得一个平滑的可定制的旋转、变换和移动设置..在一个XZ平面ofc..Y轴是UP并且总是0..

如果您有任何建议或更改或想法以使事情变得更好,请随时对此发表评论。我总是乐于改进。感谢您的回复,并希望这对很多新开发人员有所帮助,我花了很长时间来收集这些东西来自网络..

PS。旋转可以直接应用到 rotX 进行即时旋转,绕过动画和旋转速度。

WithRegards MatriXz

于 2013-02-26T14:41:06.853 回答