0
\$\begingroup\$

I am making a 2D game in Unity, and from what I understand, their convention is that an angle of 0 is up and increasing the angle rotates the object counter-clockwise.

Sidenote: I made this assumption by looking at the rotation.z value in a transform's Inspector window. When it is 0, the object's transform.up points towards positive y. Increasing rotation.z rotates the object counter-clockwise, i.e. the object's transform.up points to negative X at an angle of 90 degrees. Let me know if this is wrong.

Considering the convention, where increasing the angle rotates counter-clockwise, what should my extension method Vector2.Rotate(this Vector2 vector, float angle) do?

My question is whether doing Vector2.up.Rotate(90) would result in a clockwise or counter-clockwise rotation, where clockwise would give Vector2.right and counter-clockwise would give Vector2.left.

At first it seemed initiative to me that it would obviously rotate clockwise. It's a positive number so therefore it rotates clockwise. I'm not sure where that bias comes from, but using Rotate(90) feels like it should rotate it to the right (clockwise).

After reasoning about it, I think it would make more sense to have Rotate(90) rotate counter-clockwise. Because the universe assumes higher/positive angle results in counter-clockwise rotation, the method must also respect that rule. But for some reason it doesn't seem intuitive to me that to rotate something clockwise, I would have to call Rotate with a negative number, Rotate(-90) // clockwise.

Is there an evidence-based way to make this decision?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Your description of Unity's convention is pretty close. But it doesn't specify that an angle 0 is "up" per se. It might be better to think of the angle 0 as meaning "unchanged" - so if you start with an up vector and rotate 0 degrees, you still have an up vector, and the same goes for a right vector, etc. We could make the argument that the angle 0 actually starts from the positive x-axis, following mathematical convention, but that only really shows up in our calculations when we're using Mathf.Atan2(y, x) \$\endgroup\$
    – DMGregory
    Commented Aug 25, 2019 at 11:24

1 Answer 1

1
\$\begingroup\$

I was tempted to vote to close this as primarily opinion-based, but we may be able to provide an evidence-based argument here.

I'd recommend that your method rotate counter-clockwise for positive angles, for the sake of interoperability, and mathematical convention:

  1. Interoperability

    As you've pointed out, Unity's API already picks a side by using left-handed coordinates and treating the z rotation angle as being about the positive z axis. So several methods of the Transform, Rigidbody2D, and Vector*/Quaternion math classes all agree that a positive z angle rotates counter-clockwise (from the default camera viewpoint).

    If we "go with the flow" then we have one less exception or special rule to remember, and fewer corrective adjustments to apply. Let's say you need to coordinate the movement of several objects of different types in your game, based on the change in angle of another vector:

    float myAngleDelta = Vector2.SignedAngle(oldReferenceVector, newReferenceVector);

    If we use Unity's convention, we can plug this value straight through into our custom method:

    Vector2 newAimVector = aimVector.Rotate(myAngleDelta);

    And we can also apply this very same value to all our other operations, without needing to negate/adjust it:

    Transform aimReticle = GetReticle();
    reticle.Rotate(0, 0, myAngleDelta);
    
    Rigidbody2D tank = GetTank();
    tank.MoveRotation(tank.rotation + myAngleDelta);
    
    Quaternion twist = Quaternion.Euler(0, 0, myAngleDelta);
    

Vector3 newAim3D = twist * aim3D;

Notice we didn't have to remember to flip myAngeDelta anywhere, across 4 different types and methods - the same calculated number works in all of these contexts. I could read an angle directly out of Rigidbody2D.rotation and feed it into your custom method, no conversion required. That alone is a big win in simplicity, clarifying the meaning of your code, and avoiding new special cases to keep track of.

  1. Mathematical Convention

    This also happens to agree with the way angles are conventionally described in a mathematical context. When working with the unit circle or polar coordinates, we say a 2D vector has the form...

    $$\vec v = r \cdot \left( \cos \theta , \sin \theta \right)$$

    (or if you want to get fancy)

    $$x + i y = r \cdot e ^ {i \theta}$$

    ...where theta is an angle counter-clockwise from the positive x-axis.

    Polar Coordinate diagram from Wikipedia https://en.wikipedia.org/wiki/Polar_coordinate_system#/media/File:Examples_of_Polar_Coordinates.svg

    This gives us a different kind of interoperability: we now match the bulk of math formulas we might find on the net or in a textbook about how to compute particular angles we care about. So we can convert them clearly into code without extra correction factors (besides the often-unavoidable conversion to & from radians)

    One such formula in particular is so common it's baked right into the standard Math or Mathf classes of many languages/environemnts:

    float radianAngle = Mathf.Atan2(vector.y, vector.x);

    Atan2(y, x), when used as directed, converts from a vector to an angle measured counter-clockwise from the positive x-axis. Once again, going with the flow helps you keep compatibility with the bulk of what's already out there.

This also gives you a third kind of interoperability: clarity to the minds of other programmers, gamedevs, or math geeks who are used to these conventions. So if you bring a new person onto your team, or post a question here for help, they'll have an easier time understanding what you're doing if you stick to convention, and their contributions are more likely to have the desired result because they haven't overlooked a hidden convention flip.

\$\endgroup\$

You must log in to answer this question.

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