Skip to main content
typo
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
  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.SingedAngle(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;

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;

 

  1. Mathematical Convention

    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 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.

  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.SingedAngle(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;
  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.

  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;

 

  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.

Adding complex number example
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401

(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.

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

(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.

Expanding with Vector2.SignedAngle example
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
  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 to match an, based on the change in angle you computed for yourof another vector:

`float myAngleDelta = Vector2.SingedAngle(oldReferenceVector, newReferenceVector);`

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

Sticking with Unity's convention,And we can also apply this very same value to all our other operations, without needing to negate/adjust it:

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. 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 Quaternion 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 to match an angle you computed for your custom extension method:

Sticking with Unity's convention, we can apply this very same value to all our other operations, without needing to negate/adjust it:

Notice we didn't have to remember to flip myAngeDelta anywhere - the same calculated number works in all of these contexts. I could read an angle 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. 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.SingedAngle(oldReferenceVector, newReferenceVector);`

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

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

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.

Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
Loading