33
\$\begingroup\$

I have objects in my game that move faster at 45 degrees then at 90 degrees.

Each object has:

  • Point (x,y) position
  • Vector2D (x,y) direction
  • Int speed

And what I do during an update, is that a new position is calculated with:

position.x += direction.x * speed
position.y += direction.y * speed

How do I correct this so that it moves with the same speed at any angle?

\$\endgroup\$
5
  • 9
    \$\begingroup\$ Normalize your direction vector before use; problem solved. \$\endgroup\$ Jan 25, 2012 at 8:49
  • 1
    \$\begingroup\$ Had to google normalizing :) found this usefull site fundza.com/vectors/normalize/index.html \$\endgroup\$
    – Jason94
    Jan 25, 2012 at 9:04
  • \$\begingroup\$ And if you are using user input to control this object, be aware of locking to the 12,3,6,9 directions as explained here for XNA devs: xona.com/2010/05/03.html. It may be something you want (such as in an RPG game) or not (such as in a Geometry Wars style game). \$\endgroup\$
    – Xonatron
    Jan 25, 2012 at 14:58
  • \$\begingroup\$ in the old game Descent, this was a feature. \$\endgroup\$
    – J. Holmes
    Jan 25, 2012 at 18:46
  • \$\begingroup\$ @32bitkid Yes, see also Doom straferunning \$\endgroup\$
    – bobobobo
    Apr 6, 2013 at 22:42

4 Answers 4

57
\$\begingroup\$

This can be explained with the Pythagorean Theorem, which is the following formula:

a² + b² = c²

In your case, when moving right, you're using (x:1, y:0) which gives us

c² = 1 + 0 = 1
c = sqrt(1) = 1.00

When moving up and right, you're using (x:1, y:1) which gives us

c² = 1 + 1 = 2
c = sqrt(2) = 1.41

So as you can see, the length diagonally is longer than the length on the cardinal axes.

As others have mentioned, you should simply normalize your direction vector. If you use XNA, it's done like this:

var normalizedDirection = direction;
normalizedDirection.Normalize();
position += normalizedDirection * speed
\$\endgroup\$
1
  • \$\begingroup\$ I'm giving you +1 for your help in my question :) \$\endgroup\$
    – Martin.
    Jan 26, 2012 at 11:52
12
\$\begingroup\$

Normalize your direction vector before use.

As explained by MindWorX, this can be simply understood, if your worried about your direction vectors possibly giving you grief, make sure they are unit vectors (magnitude/length of 1).

Length(Vector2(1, 1)) == 1.4142135623730951 // first hint of grief
Length(Vector2(1, 0)) == 1

Vector2(1, 1) * 2 == Vector2(2, 2)
Vector2(1, 0) * 2 == Vector2(2, 0)

Length(Vector2(2, 2)) = 2.8284271247461903 // second hint
Length(Vector2(2, 0)) = 2

If normalized:

normal(Vector2(1, 1)) == Vector2(0.707107, 0.707107)
Length(Vector2(0.707107, 0.707107)) == 1 // perfect
\$\endgroup\$
3
  • 14
    \$\begingroup\$ Not a helpful answer. If the questioner knew what "normalize your direction vector" meant, he would not have asked the question. \$\endgroup\$ Jan 25, 2012 at 13:14
  • \$\begingroup\$ @KristopherJohnson it was not clear the questioner did not know how to normalize a vector. Albeit the questioner seems resourceful enough that it did not matter anyway. \$\endgroup\$ Jan 25, 2012 at 17:00
  • 2
    \$\begingroup\$ @KristopherJohnson: if the questioner didn't know what "normalize your direction vector" means, he just need to type that to google, append the name of his language, and get a code with explanations. \$\endgroup\$
    – Lie Ryan
    Jan 26, 2012 at 6:25
7
\$\begingroup\$

How are you calculating your direction? If 45 degrees is (1,1), then it's certainly going to be faster than 90 degrees (1,0).

I suggest you use something like this:

direction.x = Math.Cos(angleInRadians);
direction.y = Math.Sin(angleInRadians);

To get the angle in radians, you'll have to multiply your degrees with PI / 180 or even better, use MathHelper. Eg.

angleInRadians = 45.0 * Math.PI / 180.0; // first method
angleInRadians = MathHelper.ToRadians(45f); //second method
\$\endgroup\$
0
7
\$\begingroup\$

Jason,

Rather than having three object attributes,

  • Point (x,y) position
  • Vector2D (x,y) direction
  • Int speed

it is often much easier to combine the direction and speed into a velocity vector. Then you have only two attributes,

  • Point (x,y) position
  • Vector2D (x,y) velocity

Updating position

When you need to update the object's position, it's as simple as:

position.x += velocity.x * Δt;
position.y += velocity.y * Δt;

where Δt is your time delta — or time difference — or time step.

Updating position and velocity

It is also very easy this way to handle acceleration (such as from gravity). If you have an acceleration vector, you can update the velocity and position together like this:

position.x += (velocity.x * Δt) + (0.5 * acceleration.x * Δt * Δt);
position.y += (velocity.y * Δt) + (0.5 * acceleration.y * Δt * Δt);

velocity.x += acceleration.x * Δt;
velocity.y += acceleration.y * Δt;

(This is basically the s = vt + ½at² formula from Physics 101.)

Applying a speed

If you want to apply a given speed in some normalized direction, you can set the velocity like this:

velocity.x = normalizedDirection.x * speed;
velocity.y = normalizedDirection.y * speed;

Deriving a speed

And if you need to do the reverse — deriving the speed and direction from a given velocity vector — you can simply use the Pythagorean theorem or the .Length() method:

speed = velocity.Length();

And once the speed is known, the normalized direction can be calculated by dividing the velocity by the speed (being careful to avoid dividing by zero):

if (speed != 0) {
    normalizedDirection.x = velocity.x / speed;
    normalizedDirection.y = velocity.y / speed;
} else {
    normalizedDirection.x = 0;
    normalizedDirection.y = 0;
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .