'How do I calculate a 2D Vector from a dot product or a signed angle?

I have the following scenario:

I am working on a top-down 2-dimensional (XZ plane) game and i need to calculate the difference of the characters movement direction (moveDir) to the look direction (lookDir).

I would like to calculate a 2D Vector (xy) that holds the following information:

The X value should range from -1 (character facing backwards) to 1 (facing forwards)

The Y value should also range from -1 (character facing left) to 1 (facing right).

To calculate the X value I can use the dot product of moveDir and lookDir. However, I dont understand how to correctly calculate the Y value. I assume that I have to use the signedAngle between moveDir and lookDir, as the signedAngle returns a value of -90 if the character is facing left and 90 if its facing right.

I could probably even use the signedAngle between the vectors for calculating both X and Y values, as a signedAngle of 0 has the same meaning as a dot product of 1 (and also the same applies to a signedAngle of 180 and the dot product of -1).

How do I calculate the missing part?



Solution 1:[1]

I figured out the following solution (pseudocode):

y = 1 - absoluteValue(dotProduct(lookDir, moveDir))

if (signedAngle(lookDir, moveDir) <= 0
    y *= -1

Solution 2:[2]

As said assuming both moveDir and lookDir are normalized vectors I think you can simply use Vector2.Dot for the first one:

var x = Vector2.Dot(moveDir, lookDir);

this will return a value between 1 (lookDir == moveDir => looking "forward") and -1 (lookDir == -moveDir => looking "back") and 0 would mean it is perpendicular.

Then for "left" and "right" you can cheat a little bit and basically do the same but using Vector3.Cross (note that even though you only want 2D vectors Unity is a 3D engine after all) in order to first obtain a vector that supposedly is "right" like e.g.

var y = Vector2.Dot(lookDir, Vector3.Cross(moveDir, Vector3.forward).normalized);

to explain this a bit

  • we use Vector3.forward which is basically (0, 0, 1) (a vector pointing into the screen away from you) since you are dealing with Vector2 vectors which move on the XY plane. So in order to get a perpendicular vector to a Vector2 we first need a vector that is perpendicular to all of them on the third axis

  • Vector3.Cross uses left hand rule

    enter image description here

    so

     Vector3.Cross(moveDir, Vector3.forward)
    

    returns a Vector pointing 90° to the "right" from the moveDir

  • Vector3 and Vector2 are implicitly convertible into each other which will simply ignore the Z component or use 0

OR as Fredy pointed out you can also simply use

var y = Mathf.Sin(Vector2.SignedAngle(lookDir, moveDir) * Mathf.Deg2Rad);

which basically does the same and is probably less complicated ;)

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ceso
Solution 2