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
\$\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\$
  • \$\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\$ – Stephane Hockenhull Aug 20 '18 at 19:22
  • \$\begingroup\$ I used this and it works! \$\endgroup\$ – karamazovbros Aug 20 '18 at 20:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.