How to get 0-360 bearing in 2D?

Hi everyone,

in my top-down 2D project I have a submarine that can rotate and move around the map. I would like to find the bearing to another 2D object, such as a ship. The 0 bearing is to the front of the submarine (its bow, or “nose”), the 90 bearing is to its right, 270 is to its left, and 180 is directly behind it.

The code I pasted below sort of works: it returns a bearing that is close to what it should be, BUT only when the player submarine is stationary. As soon as it starts to move, the values do not change as they should. Essentially, this is an example of the value that should be returned after the submarine has rotated:

I have tried using this approach: first I determine if the object is to the left or right of the submarine, then I find the angle, and finally show that angle in my game. However, this isn’t working, i.e. the wrong bearing is shown.

I’d appreciate any help or ideas on how to approach this problem.

Here’s the code that I tried to refactor for 2D from this link: link text

int AngleDir(Vector3 forwardVector, Vector3 targetDirection, Vector3 upVector) {

float direction = Vector3.Dot(Vector3.Cross(forwardVector, argetDirection), upVector);

if (direction > 0f) {
       return 1;
} else if (direction < 0f) {
       return -1;
} else {
       return 0;
   }
}


// Takes a starting object's Transform, and a target object's position, and returns the angle to the target 0-359° 
public float GetBearing(Transform startTransform, Vector3 targetPosition) {

Vector3 vectorToTarget = targetPosition - startTransform.position;

float angleToTarget = Vector3.Angle(startTransform.right, vectorToTarget);

int direction = AngleDir(startTransform.right, vectorToTarget, startTransform.up);

return (direction == 1) ? 360f - angleToTarget : angleToTarget;
}

What does angledir do?
And try splitting it into multiple lines, it looks nicer when its compact but it should be much easier to read and debug if it’s spread over multiple lines.
You may also wan’t to look at Transform.InverseTransformPoint, it transforms a point from world space to local space. It might make it a bit easier to calculate the relative angle if you do that.

Hi @Sartoris,

If you are using a topdown 2d view, are you by chance in a 3d view setting? Then you x/y coordinates in ScreenSpace are actually x/z in world space.

Your code then becomes

float angle = Mathf.Abs(Mathf.Round(Mathf.Atan2(target.position.z - player.position.z, target.position.x - player.position.x) * Mathf.Rad2Deg));

@Zimbwawa Hey Zimbwawa, sorry about the messy outline. I just reworked the post to make it read more easily, I hope that it’s more understandable now. Since I posted this question I found a link here on the forum that offers one solution, but in 3D. I tried reworking it for 2D, and I posted that code instead. However, I’m not sure if I’m doing it right, as the values that are returned are not correct. Thank you for your suggestion about InverseTransformPoint, I’ll see if I can apply that.

@Klarzahs Hey Klarzahs, thanks for answering, but I’m working in the 2D camera space environment, and everything else is working in that regard, so I don’t think the solution is in exchanging z for y.

In the meantime I’ve changed the code to this:

Vector3 direction = target.position - player.position;

float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

float bearing = (angle + 360) % 360;    

And it works, but the bearing does not change when the sub rotates. Instead, the program thinks that the sub is constantly pointing to the right, and reports the bearing based on that assumption. I would need a way to let the code know that it needs to take the rotation into consideration.