Reputation: 25
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. I knew how to normalize this in Unity but not in 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.
Upvotes: 1
Views: 1814
Reputation: 36649
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. 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.
Upvotes: 5
Reputation:
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. 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
.
In MonoGame, attempting to draw using a Vector2 containing a NaN
, results in the sprite not being drawn.
I will address 8-way movement, 4-way movement, and arbitrary movement:
For only eight directions of movement, the fastest code without producing a NaN
is:
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".
For arbitrary velocities using normalization without NAN
s:
if(_velocity.LengthSquared() > 0)
{
_velocity.Normalize()
_velocity *= _speed;
_position += _velocity;
}
Upvotes: 0
Reputation: 1
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". However, the diagonals should not ALSO be 1 pixel per "step", because this is effectively combining both rates together. I.e. 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. This is where you need trigonometry. You need to find the x and y distance of a circle, multiplied by your targeted rate. You said 1, so we'll go with that. 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. 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. Note that the 0.71 numbers aren't some random number. It's specifically the result of a pythagoreon algebra (you know, that a^2 + b^2 = c^2 you learned in high school). Also do note that these calculations use degrees. C# probably expects radians instead, so plan around that.
This 45 degree unit circle might clarify things as well: https://etc.usf.edu/clipart/43200/43202/unit-circle10_43202_lg.gif
Upvotes: 0