How Mathf.Atan2(x,y) works

Hi ım adding speed to a stick and ı want it to turn to this vector and ım using mathf.atan function but when ı used it something was wrong ı had to add 90 and put - to atan2 function why this happened here is the code GameObject pykee = Instantiate(pike, transform.position, Quaternion.Euler(0, 0, -Mathf.Atan2(x, y) * Mathf.Rad2Deg+ 90));

in normal stick looks to left side ı thing gameobject has to be look certain direction

The Atan2 method is just a quadrant corrected version of the normal Atan trigonometric function. Atan2 essentially just calculates Atan(y / x) and then based on the signs of x and y chooses the right quadrant so it can actually give you a full 360° circle. Mathematically all trigonomatrix functions like sin, cos, tan, Asin, Acos, Atan, … work with 0° being the positive x axis and positive angles rotate counter clockwise. See any unit circle diagram that shows the values visually.

The tangens is essentially the slope of a line that describes an angle of “a” with the x axis.

So your conversion shifts the angle by 90° and also makes the angle clockwise instead of counterclockwise.

I don’t think there’s much more to say about this. Except you’re actually using Atan2 wrong. The first argument is y, the second argument is x because internally it calculates y / x. So you currently calculate the Atan of x/y which would kinda flip your result on the diagonal.

Note that the trigonometric functions work the same way all around earth. If they don’t do what you want, you use them wrong / expects the wrong results. Unity uses a left handed coordinate system with z being forward. There are many other coordinate spaces you could use. In mathematics we generally use a right handed coordinate system where z is pointing backwards. So if you want to convert between different coordinate systems, you have to think about what should go where.

2 Likes

yeah it works when ı change the x and y but this is not make senses in math first argument always would be x

Actually, tangent is “opposite over adjacent”, or y / x in the traditional unit circle, with 0 degrees facing right.

SOH CAH TOA… learn it, live it, love it!

EDIT - Thanks to ADNCG for picking out my typo!

1 Like

Well it does make sense because you actually calculate ATan(y/x). It’s just how this special version od Atan is defined. If you like to discuss something that is a defacto standard all across the world, here’s the SO question for this. Likewise the wikipedia article really tries hard to find cases where the order is “x,y” but only comes up with spread sheet software or calculators which are generally designed for abstract / human-friendly interactions. In literally almost any programming language that has a 2 argument atan function the order is (y, x) (even in shader code). Just like all trig function use radians and not degree. Calculators most of the time use degree, again, to be human friendly when in reality angles are naturally defined in radians.

Anyways, if you think this order is too confusing you are free to use something like:

float a = Mathf.Atan(y/x);
if (x < 0)
{
    if (y < 0)
        a -= Mathf.PI;
    else
        a += Mathf.PI;
}

Now you have successfully recreated and implemented atan2 yourself inline. Just remember that you have to divide y by x. Though that’s probably much simpler than remember to use Atan2(y, x)

Though note that my “inline” implementation has one “tiny” difference. In the degenerate case of (0, 0) Atan2 returns 0 while this results in “NaN”. So to properly implement Atan2 you should check for that special case as well.
My Atan2

// reversed Atan2
public static float Atan2R(float x, float y)
{
    if (x == 0f && y == 0f)
        return 0f;
    float a = Mathf.Atan(y / x);
    if (x < 0f)
    {
        if (y < 0f)
            a -= Mathf.PI;
        else
            a += Mathf.PI;
    }
    return a;
}

Of course if you just want a “reversed” version, you could also just do

public static float Atan2R(float x, float y) => Mathf.Atan2(y, x);

Though if anything would make sense in the context of Unity, you probably want something like:

public static float Atan2(Vector2 v) => Mathf.Atan2(v.y, v.x);
1 Like