0
\$\begingroup\$

What I want to know is if I can calculate an angle similar to how I'm doing in 2D and update the x,y,z position of a bullet based on that angle in a similar manner I'm doing the update function below.

I wrote this function in Ruby to find the target angle between two 2D (x,y) vectors, but now I want to find out how to do this in 3D in a similar way:

  def target_angle(point1, point2)
    x1 = point1[0]
    y1 = point1[1]
    x2 = point2[0]
    y2 = point2[1]
    delta_x = x2 - x1
    delta_y = y2 - y1
    return Math.atan2(delta_y, delta_x)
  end

Given an object (like a bullet in this case), I can shoot the object given a target_angle between the player (x,y) and the mouse (x,y), as such in the bullet update function:

  def update
    wall_collision
    # the angle here is the target angle where point 1 is the player and
    # point 2 is the mouse
    @x += Math.cos(angle)*speed
    @y += Math.sin(angle)*speed
  end

Is there a similar method to calculate a target angle in 3D and use that angle in a similar manner as my update function (to shoot a bullet in 3D)? How can I make this work for two 3D vectors (x, y, z), where you have the player position (x,y,z) and some other arbitrary 3d point away from the player.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Instead of finding the angle using atan2 and then reconstructing the vector using sin and cos you can store the direction using a normalised vector.

This works in both 2D and 3D.

  def target_angle(point1, point2)
    x1 = point1[0]
    y1 = point1[1]
    x2 = point2[0]
    y2 = point2[1]
    @delta_x = x2 - x1
    @delta_y = y2 - y1

    len = Math.sqrt(@delta_x*@delta_x+@delta_y*@delta_y);
    @delta_x = @delta_x / len;
    @delta_y = @delta_y / len;
  end

  def update
    wall_collision
    # the angle here is the target angle where point 1 is the player and
    # point 2 is the mouse
    @x += @delta_x*speed
    @y += @delta_y*speed
  end

To do it in 3D just add the math for z the same way x and y are calculated.

len = Math.sqrt(@delta_x*@delta_x + @delta_y*@delta_y + @delta_z*@delta_z);

You can even ditch speed and store it right inside delta_x, delta_y, and delta_z by multiplying them by speed after the division by len to normalise them into a unit vector.

    @delta_x = @delta_x / len * speed;
    @delta_y = @delta_y / len * speed;

...

    @x += @delta_x
    @y += @delta_y

So the whole thing in 3D becomes:

  def target_angle(point1, point2)
    x1 = point1[0]
    y1 = point1[1]
    z1 = point1[2]
    x2 = point2[0]
    y2 = point2[1]
    z2 = point2[2]
    @delta_x = x2 - x1
    @delta_y = y2 - y1
    @delta_z = z2 - z1

    len = Math.sqrt(@delta_x*@delta_x + @delta_y*@delta_y + @delta_z*@delta_z);
    @delta_x = @delta_x / len * speed;
    @delta_y = @delta_y / len * speed;
    @delta_z = @delta_z / len * speed;
  end

  def update
    wall_collision
    # the angle here is the target angle where point 1 is the player and
    # point 2 is the mouse
    @x += @delta_x
    @y += @delta_y
    @z += @delta_z
  end

This way 2D is just 3D where z is always zero (so it is removed). The code is otherwise identical in working principle.

\$\endgroup\$
2
  • \$\begingroup\$ Note: speed could be multiplied once by baking it into len by doing len /= speed before the 3 delta_xyz divisions but I wanted to keep the example simple and not refactor too much of the code. Using a reciprocal for len and 3 multiplications instead would also be faster, but again: KISS for the sake of example. \$\endgroup\$ Aug 20, 2018 at 19:22
  • \$\begingroup\$ I used this and it works! \$\endgroup\$ Aug 20, 2018 at 20:29

You must log in to answer this question.

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