How can I normalize my diagonal movement

I’m very new to this sorry if this is a bit of a noob question.

I have some simple movement code written but I would like to normalize it so the diagonal movement is the same speed as the movement on the X and Y axis.

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;
        }
}

Sum up all directions, then normalize this result vector in the end to get the normalized vector which you can then use to calculate your new position.

Gotta use trigonometry

I’ll see if I can find out how it’s done specifically later, but I know off the top of my head that you do that with trig.

What Kwyrky said, but Monogame does all this for you in Vector2. You can just set the components, then normalize the vector.

float speed = 100; // pixels per second
Vector2 dir = Vector2.Zero;
if (Keyboard.GetState().IsKeyDown(Keys.W))
  dir.Y -= 1;
... // and so on... modify dir per your input so that it will result in X and Y being either -1, 0, or 1.

if (dir != Vector2.Zero) // Not sure off the top of my head if dir.Normalize() handles 0,0 already... easy to check :D
  dir.Normalize(); // This will normalize your direction vector to a length of one... which you can now multiply by your movement speed.

_position += dir * gameTime.Elapsed.TotalSeconds * speed;

I might have gotten a variable wrong or two in here, but hopefully you get the idea :wink:

1 Like

I got this code that was suggested from another site which is very similar to yours,

var dir = Vector2.Zero;

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

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

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

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

        dir.Normalize();
         
        var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
        _position += dir * speed * deltaTime; 

The only problem is that it seems to make the sprite that this code is controlling disappear. I have this in the LoadContent that might be causing it

_position = new Vector2(0, 0);

LoadContent is only called once on startup, so setting your sprite to an initial position of zero wouldn’t be the cause.

If I had to guess, it would be that your move puts the sprite in a position off the screen. You can verify this by setting a breakpoint in the Draw method, where you draw the sprite, and verifying the value of _position for the SpriteBatch.Draw call.

What you don’t show here is how you use dir to calculate your new position. It may help for you to both take a look at that, and post your code here :slight_smile:

Here is the code for the Draw

_spriteBatch.Begin();
_spriteBatch.Draw(_texture, _position, Color.White);
_spriteBatch.End();

From what I can tell the the dir shouldn’t move it from the 0,0 spawn. The result of the breakpoint says the _position is NaN.

I suspect it’s your update code. Up above I had a check to see if dir was Vector2.Zero before I called Normalize, and a comment saying I wasn’t sure if it was needed or not. It sounds like it’s needed :wink:

What’s happening is that you’re multiplying your position by NaN (Not a Number), which in turn makes your position NaN. Just make sure your dir is not 0,0 before you call Normalize.

If that still has problems, post your Update code (not your Draw code).

Here is the update code I currently have, I noticed that if I changed the Vertor2.Zero to Vector2.One my sprite appeared but didn’t respond to inputs and was moving very slowly towards the bottom right of the screen, very odd :laughing:

 protected override void Update(GameTime gameTime)
    {
         _tiledMapRenderer.Update(gameTime);


        var dir = Vector2.Zero;

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

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

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

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

        var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
        _position += dir * speed * deltaTime;

        base.Update(gameTime);
    }

Yea, like I said, change this…

dir.Normalize();

… to this …

if (dir != Vector2.Zero)
  dir.Normalize();

(Obviously restoring your initial value of dir to Vector2.Zero).

There’s a reason why changing it to Vector2.One results in that behaviour, but I’ll let you work it out :slight_smile:

1 Like

The sprite is there but not responding to input. I have a feeling is because of the way I defined speed in the Game1

int speed = 1;

I got it, changed it to

public float speed = 100;

Yea, but keep in mind that speed is just a movement in pixels per second. Maybe take a minute to look over the code and understand how it works. You may want to use a different movement speed depending on your needs :slight_smile:

Sounds like you’re on your way though, good luck!

1 Like

Thanks for your help!

1 Like

Mark the solution post please.

Here’s a proper version of Normalize that doesn’t break half of your game when encountering a zero vector. I recommend just using it over default Normalize.

1 Like

I do find it odd that this isn’t baked right into MG itself, but I suspect this is how XNA worked and it’s my understanding that MG aims to faithfully reproduce XNA.

Out of curiosity, has anybody encountered a case where you’d actually want a zero’d vector to normalize to NaN?

Probably for performance

I’ve never had to call vector.normalise when there’s a zero vector, as you would only be doing a movement update when something is moving

1 Like

Having a NaN returned when you try to divide by zero is very useful for debugging. If you are trying to normalize a zero length vector in your code then your logic has a flaw in it. This is especially the case when you have a vector that you assume is never zero length. Either your assumption is wrong or the code creating the vector is wrong. In either case, the NaN alerts you to the bug and makes it easier to track down and fix.

4 Likes