Dave Dave - 11 months ago 43
Javascript Question

How to calculate a path to the mouse?

I have virual world:

var worldSize = [10000, 10000]; //width, height

Next I have player's position:

var currentPlayerPosition = [5000, 5000]; //center on the map

enter image description here

Now I need every 10ms check current mouse position, and calculate direction to mouse, and update
to direction result -1px (or +1px if mouse is under player...)
. The same as right/left side.

Edit: I dont need code, I need mathematica to calculate direction and then substract/add pixels to array of player's position

Edit 2

Example what I mean:

then what I must math from
every 10ms if I want move player to mouse position?

currentPlayerPosition[0] = currentPlayerPosition[0]-(what_number_here)
currentPlayerPosition[1] = currentPlayerPosition[1]-(what_number_here)


currentPlayerPosition[0] = currentPlayerPosition[0]+(what_number_here)
currentPlayerPosition[1] = currentPlayerPosition[1]+(what_number_here)

Answer Source


Forget the equation of the line, slope and intercept won't buy you much in game development. Use Vectors!

So, you have two points

player: (700, 250)
mouse: (800, 100)

And you want the vector from one to the other. That's the difference:

Difference: player - mouse
Difference: (700, 250) - (800, 100)
Difference: (700 - 800, 250 - 100)
Difference: (-100, 150)

Errata: since you will be moving player with the code (and not the mouse), we need the distance from player to mouse, so calculate mouse - player instead. This is because we will add the (manipulated) difference vector to the player vector later.

For abstract what you do next is calculate the norm and the unit vector. Then - probably - use scale the unit vector to get an advance vector to add to the position.

Euclidean geometry

Ok, now, how long is that vector? That's the norm of the vector:

Distance: Norm(Difference)
Distance: Norm((-100, 150))
Distance: sqrt((-100 * -100) + (150 * 150))
Distance: 180.277563...

Note: that's Euclidean distance (a.k.a. Pythagoras theorem, generalized for more than 2D, but we are in 2D anyway).

What direction is it? Well, it is negative 100 on the horizontal and 150 on the vertical... So, it is on the Quadrant 2.

We can represent the direction as the unit vector of the difference (that is a vector of the same direction and length 1):

Direction: Difference/Distance
Direction: (-100, 150)/180.277563...
Direction: (-100 / 180.277563..., 150 / 180.277563...)
Direction: (-0.554700..., 0.832050...)

It may be harder to make sense of the values of unit vector. Just remember that the unit vector keeps the same direction.

The reason why the unit vector is useful is because then you can scale it to whatever distance you need. For example if you want to move 10 distance units you scale the unit vector by 10 and that is the value you have to add to the position.

So the advance vector would be the unit vector multiplied by the distance you want to advance.

I'll talk about the slope a bit for ease of understanding, what you do to calculate the slope is as follows:

Slope: Y/X
Slope: 150/-100
Slope: -1.5

The slope as you can see is the rate of change of the Y over the X (that is, how much does the Y change for each unit of X). So -1.5 means that for each unit you advance on the X axis you move -1.5 units on the Y axis... that's the direction.

You may prefer an angle:

Angle: Atan(Slope)
Angle: Atan(-1.5)
Angle: -0.982794...

Note 1: that value is in radians. You can covert it to degrees if you want.

When implementing use Atan2 if available in the standard library of your language. It will take the X and Y values and will work around what would have been a division by 0 calculating the slope. Edit: futhermore, if you use Atan the angle will be on the first or fourth quadrant. To understand this consider that -1/2 and 1/-2 are the same slope, but (2, -1) and (-2, 1) aren't the same vector (they aren't the on same direction either).

So, use Atan2 instead:

Angle: Atan2(Difference)
Angle: Atan2(Y, X)
Angle: Atan2(150, -100)
Angle: 2.158798...


-0.982794 radian = -56.3099° (degrees) [Quadrant 4]
2.158798 radian = 123.69° (degrees) [Quadrant 2]

The angle isn't of much use in the implementation actually. You will find yourself working with the unit vector as explained above.

Manhattan geometry

Manhattan geometry (a.k.a. Taxicab geometry) is a geometry in which things only move along the axis. So you don't really have diagonals or varied angles. Just up, down, left and right (in 2D, that is).

If you are working in Manhattan geometry you calculate the norm as follows:

Distance: Norm(Difference)
Distance: Norm((-100, 150))
Distance: abs(-100) + abs(150)
Distance: 250

That is by adding the absolute values of the length of the vector along each axis. The reason is that the object will have to move on one axis and then the other.

Now, the unit vector is rarely used when implementing a game in Manhattan geometry. Instead what you may do is take the absolute values to decide over what axis to move first. The axis on which the vector has more absolute value should move first (or last, depending on what you want).

Alternatively you may move first always on the same axis and then the other. It is trickier to test when the destination has been reached on a single axis, it requires to check the distance per axis independently - and will run into the oscillation problem described below, plus the problem of comparing floating point numbers.

So, the advance vector would be the distance you want to move (with the sign of the difference vector) over the axis you decided to move first and 0 on the other axis.


You are checking the position at fixed intervals - although it is worth to measure the time passed from the last check, because computer performance may vary - meaning that you have the time.

Say, if you want to move 10 distance units per unit of time, but that time that passed is actually 3 units. Then the distance is 3 * 10 = 30 units. And that is what you use to scale the advance vector.

Regardless, what is actually happening is that the object is jumping from a position to the other, as you update the position. This will have a probably unexpected consequence: the object will inevitably overshoot. It will go beyond the target point and then try to go back and then go beyond again, oscillating back and forth... to prevent this you will check if the norm of the distance vector is less than the distance you will advance, and if it is less... just put the object at the destination.

Please note that if you want flock behaviors, collision detection, acceleration, etc. This part of the code will need to be updated.

A note on screen coordinates: For historical reasons the computers place the coordinate’s origin on the top left corner. So when you have a positive value on the horizontal axis, it is going to the right. And a positive value on the vertical axis, it is going downwards. Unless you are working under a transformed coordinate system... in which case it is whatever the transformation says, in doubt, do experiment.

Working example

var time = (new Date()).getTime(); // time in milliseconds

var player_element = document.getElementById('player');
player_element.style.left = 0;
player_element.style.top = 0;

var mouse = [0, 0]; // pixels
var player = [0, 0]; // pixels
var speed = 100.0; // pixels per second

// get the mouse pointer position
   $(document).on('mousemove', function(e){
      // update mouse
      mouse[0] = e.pageX;
      mouse[1] = e.pageY;

function update()
  // calculate the difference
  let diff = [mouse[0] - player[0], mouse[1] - player[1]];
  // calculate the norm
  let norm = Math.sqrt(diff[0] * diff[0] + diff[1] * diff[1]);
  if (norm == 0)
    // already at target or not yet initilized
  // calculate the unit vector
  let unit = [diff[0] / norm, diff[1] / norm];
  // get elapsed time from last iteration
  // divided by 1000 to get seconds
  let new_time = (new Date()).getTime();
  let elapsed = (new_time - time) / 1000.0;
  // calculate advance by scaling unit vector
  let advance = [unit[0] * elapsed * speed, unit[1] * elapsed * speed];
  // calculate the norm of the advance vector
  let advance_norm = Math.sqrt(advance[0] * advance[0] + advance[1] * advance[1]);
  // check if we are about to overshoot
  if (advance_norm > norm)
    // we are too close to target
    // just set the position to the target
    player[0] = mouse[0];
    player[1] = mouse[1];
    // there is still room to go
    // advance
    player[0] += advance[0];
    player[1] += advance[1];
  // update player and time
  player_element.style.left = (player[0]) + "px";
  player_element.style.top = (player[1]) + "px";
  time = new_time;

setInterval(update, 40);
  background: green;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="player"></div>