简体   繁体   English

如何在 Monogame 中标准化我的对角线运动

[英]How can I normalize my diagonal movement in Monogame

I have some simple movement code and the only problem I have with it is the diagonal movement is faster then the X and Y movement.我有一些简单的运动代码,唯一的问题是对角线运动比 X 和 Y 运动更快。 I knew how to normalize this in Unity but not in Monogame.我知道如何在 Unity 中标准化这一点,但在 Monogame 中却不知道。

private Vector2 _position;

protected override void Update(GameTime gameTime)
{
     

    if (Keyboard.GetState().IsKeyDown(Keys.W))
    {
        _position.Y -= 1;
    }

    if (Keyboard.GetState().IsKeyDown(Keys.S))
    {
        _position.Y += 1;
    }

    if (Keyboard.GetState().IsKeyDown(Keys.A))
    {
        _position.X -= 1;
    }

    if (Keyboard.GetState().IsKeyDown(Keys.D))
    {
        _position.X += 1;
    }
}

This should be all the relevant code, let me know if you need more.这应该是所有相关代码,如果您需要更多,请告诉我。

You should probably do something like this:你可能应该做这样的事情:

var dir = Vector2.Zero;
if (Keyboard.GetState().IsKeyDown(Keys.W))
{
    dir .Y -= 1;
}
// Same for all keys
....

// skip further processing if no keys are pressed.
if(dir == Vector.Zero)
    return;

// Ensure the vector has unit length
dir.Normalize(); 
// Define a speed variable for how many units to move
// Should probably also scale the speed with the delta time 
var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
_position += dir * speed * deltaTime; 

I'm not familiar with monogame specifically.我对monogame不熟悉。 But the overall approach should be to compute a movement direction, normalize it, and scale it to the appropriate speed, and this should be valid in any kind of game.但总体方法应该是计算运动方向,对其进行归一化,并将其缩放到适当的速度,这在任何类型的游戏中都应该是有效的。

Some of the other answers on this subject are way overcomplicated.关于这个主题的其他一些答案过于复杂。 This is a very simple and easy problem to solve - if you understand some simple trig.这是一个非常简单易解决的问题 - 如果您了解一些简单的三角函数。

I kind of like this topic because it brings up something I like to call "square diagonal vs circle diagonal".我有点喜欢这个话题,因为它提出了我喜欢称之为“方形对角线与圆形对角线”的东西。 Your x and y (up, down, left, right) movements are indeed correct at the rate of 1 pixel per "step".您的 x 和 y(上、下、左、右)运动确实以每“步”1 个像素的速度正确。 However, the diagonals should not ALSO be 1 pixel per "step", because this is effectively combining both rates together.然而,对角线也不应该是每“步”1个像素,因为这有效地将两个速率结合在一起。 Ie the "square diagonal", which is why the diagonals are faster with this approach.即“方形对角线”,这就是为什么使用这种方法对角线更快的原因。

What you want is the "circle diagonal".你想要的是“圆形对角线”。 This is where all 8 "sides" have an equal rate.这是所有 8 个“边”具有相同比率的地方。 This is where you need trigonometry.这就是你需要三角函数的地方。 You need to find the x and y distance of a circle, multiplied by your targeted rate.您需要找到一个圆的 x 和 y 距离,乘以您的目标比率。 You said 1, so we'll go with that.您说的是 1,所以我们将使用 go。 Let's assume that you have a standard mathematical coordinate system (I don't know what Unity's or monogame's is, so sorry) where 0 degrees is the positive X-axis (right) and the degree increases counterclockwise.假设您有一个标准的数学坐标系(我不知道 Unity 或 monogame 是什么,很抱歉),其中 0 度是正 X 轴(右),度数逆时针增加。 I'll try and keep it simple here with a cheatsheet.我将尝试使用备忘单保持简单。

Displacement/rate of changes for given angles:给定角度的位移/变化率:

// Right
X += 1*cos(0) // +1.00
Y += 1*sin(0) // +0.00

// Up right 
X += 1*cos(45) // +0.71
Y += 1*sin(45) // +0.71

// Up 
X += 1*cos(90) // +0.00
Y += 1*sin(90) // +1.00

// Up-left
X += 1*cos(135) // -0.71
Y += 1*sin(135) // +0.71

// Left
X += 1*cos(180) // -1.00
Y += 1*sin(180) // +0.00

// Down-left
X += 1*cos(225) // -0.71
Y += 1*sin(225) // -0.71

// Down
X += 1*cos(270) // +0.00
Y += 1*sin(270) // -1.00

// Down-right
X += 1*cos(315) // +0.71
Y += 1*sin(315) // -0.71

Mathematically, this denotes the relationship of the opposite side (the y component) and adjacent side (the x component) of triangle when it's hypotenuse (the total movement) is 1.0.在数学上,这表示当斜边(总移动)为 1.0 时,三角形的对边(y 分量)和相邻边(x 分量)的关系。 Note that the 0.71 numbers aren't some random number.请注意,0.71 数字不是随机数。 It's specifically the result of a pythagoreon algebra (you know, that a^2 + b^2 = c^2 you learned in high school).它特别是勾股代数的结果(你知道,你在高中学到的 a^2 + b^2 = c^2)。 Also do note that these calculations use degrees.另请注意,这些计算使用度数。 C# probably expects radians instead, so plan around that. C# 可能需要弧度而不是,因此请计划一下。

This 45 degree unit circle might clarify things as well: https://etc.usf.edu/clipart/43200/43202/unit-circle10_43202_lg.gif这个 45 度单位圆也可以澄清事情: https://etc.usf.edu/clipart/43200/43202/unit-circle10_43202_lg.gif

A velocity vector indicates both direction and speed.速度矢量指示方向和速度。
I encourage you to embrace velocity vectors.我鼓励你接受速度向量。

There is one caveat, a velocity vector with a zero speed(0,0), cannot have a direction.有一个警告,零速度(0,0)的速度矢量不能有方向。 Attempting to get a direction through the Normalize() function on a Zero vector (division by zero) produces a special float value called NaN any numeric operation against an NaN results in a NaN .尝试通过Normalize() function 在零向量(除以零)上获取方向会产生一个特殊的浮点值,称为NaNNaN的任何数字运算都会导致NaN

In MonoGame, attempting to draw using a Vector2 containing a NaN , results in the sprite not being drawn.在 MonoGame 中,尝试使用包含NaN的 Vector2 进行绘制会导致精灵不被绘制。

I will address 8-way movement, 4-way movement, and arbitrary movement:我将介绍 8 向移动、4 向移动和任意移动:

For only eight directions of movement, the fastest code without producing a NaN is:对于只有八个运动方向,不产生NaN的最快代码是:

private Vector2 _position;
private Vector2 _velocity = new Vector2();
private float _speed = 1; // code allows for any speed
private KeyboardState _ks;
// pre-calculate adjustment constant
private const float ONE_OVER_SQRT_OF_2 = 0.70710678118f;  // 1/Math.Sqrt(2);

protected override void Update(GameTime gameTime)
{
    _velocity = new Vector2(); // reset to zero
    _ks = Keyboard.GetState();  //local variables are much faster than function calls

    if (_ks.IsKeyDown(Keys.W))
    {
        _velocity.Y -= _speed;
    }

    if (_ks.IsKeyDown(Keys.S))
    {
        _velocity.Y += _speed;
    }

    if (_ks.IsKeyDown(Keys.A))
    {
        _velocity.X -= _speed;
    }

    if (_ks.IsKeyDown(Keys.D))
    {
        _velocity.X += _speed;
    }

    if(_velocity.LengthSquared() > _speed * _speed) // _speed*_speed = 1 with a _speed = 1
    {
       _velocity *= ONE_OVER_SQRT_OF_2; // adjust for 2 directions pressed at same time.
    }
     
    _position += _velocity; // update position with an approximate, due to float limitations, length of speed vector
}

For four directions the code simplifies to:对于四个方向,代码简化为:

private Vector2 _position;
private Vector2 _velocity = new Vector2();
private float _speed = 1; // code allows for any speed
private KeyboardState _ks;

protected override void Update(GameTime gameTime)
{
    _velocity = new Vector2(); // reset to zero
    _ks = Keyboard.GetState();  //local variables are much faster than function calls

    if (_ks.IsKeyDown(Keys.W))
    {
        _velocity.Y -= _speed;
    }

    else if (_ks.IsKeyDown(Keys.S))
    {
        _velocity.Y += _speed;
    }

    else if (_ks.IsKeyDown(Keys.A))
    {
        _velocity.X -= _speed;
    }

    else if (_ks.IsKeyDown(Keys.D))
    {
        _velocity.X += _speed;
    }     

    _position += _velocity; // update position with a length of speed
}

Note the order of the if s indicates the order of a keys evaluation "W" overrides "S".请注意if s 的顺序表示键评估“W”覆盖“S”的顺序。

For arbitrary velocities using normalization without NAN s:对于使用没有NAN的归一化的任意速度:

if(_velocity.LengthSquared() > 0)
{
     _velocity.Normalize()
     _velocity *= _speed;
     _position += _velocity;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM