How to get a 360 degree Vector3.Angle?

I am creating a flight sim compass and have run into a wall with Vector3.Angle. I have a north angle that I am checking against the transform’s rotation in a fixed update. Everything is working fine… except it is returning the angle offset instead of a “polar” offset. If I face 20 degrees left of the angle, it returns 20, thats ok. But when I turn right 20 degrees, it also returns 20. Is there a way to get it to return a value in the entire 360 degree range or is it hardcoded as an offset?

1 Like

Vector3.Angle returns the acute angle between the two vectors. There is some code in this thread that shows how to tell if one vector is to the left or to the right of another. You can get the angle with Vector3.Angle, but if it is to the left, then subtract it from 360 to get the bearing from the forward vector.

That works perfictly for a fixed rotation user, such as the FPS Player, however I am having trouble converting it to measure only the horizontal vector. My transform is a ship so it’s pitch is also a factor. Is there a way to “project” a 3d vector into 2d space, effectivly making the magnitudes irrelelvent? The compass works fine when I use only Roll and Yaw, but when I pull the Pitch up or down it changes the compass value (not good).

Here are my two scripts. The first script calculates everything, while the second reads it and ouputs the heading into debug.log

// This script is named CompassHeading

var northMarker : Transform;
static var dirNum: float;
private var heading: Vector3;

function Update () 
{
	
	 heading.z = (Vector3.Angle(northMarker.position, transform.forward));
	dirNum = AngleDir(transform.forward, heading, transform.up);

}


function AngleDir(fwd: Vector3, targetDir: Vector3, up: Vector3) {
	var perp: Vector3 = Vector3.Cross(fwd, targetDir);
	var dir: float = Vector3.Dot(perp, up);
	
	if (dir > 0.0) {
		return 1.0;
	} else if (dir < 0.0) {
		return -1.0;
	} else {
		return 0.0;
	}
}
var heading : int;

function Update ()
{
// Calculating Heading.
	
	
	if( CompassHeading.dirNum <= 0 )
	{
		heading = (Vector3.Angle(Vector3(0,0,CompassHeading.northOffsetAngle), flyer.forward));
	}
	else
	{
		heading = 360 - (Vector3.Angle(Vector3(0,0,CompassHeading.northOffsetAngle), flyer.forward));
	}
	Debug.Log(heading + "     " + CompassHeading.dirNum);

}

5 hours of torment and I finaly figured it out.

heading = parseInt(player.eulerAngles.y);

5 hours of researching Dot Products, Cross Products, Vector Projections and the Physics of 3D Space all for that little bit of code. Turns out Unity had it the whole time. Apparantly something called a Quaternion… Anyway it returns the 360 degree angle between the transform’s Y-rotation and 0 degree world space. Everything I needed all contained in one line of code…

3 Likes

Another way to do this is to convert the target into local coordinates relative to your object. ie: targetInLocal = transform.InverseTransformPoint (Target)

If the target is to the left of you, the x axis will be <0, right if its >0. Same goes for above you (y axis) or in front/behind you (z axis).

But plenty of ways to skin the cat in Unity!

Mirage: Sometimes it’s always the easy solutions that escape our sight in the midst of frustrated coding. :slight_smile:

As Mirage said, the “*.eulerAngles.y” is the exact thing which returns the whole rotation and not just the differentiation.

Thanks Mirage. I was looking for this… :smile:

Hi,I am very interest in the way to judge one vector is to the left or to the right of another, but the links seems has already broken. Can you please post another one for this topic?

More simple,

using UnityEngine;
using System.Collections;

public class script1 : MonoBehaviour {

    public Transform target;
    public Transform body;
    public string side;


    void Update () {

        if(Vector3.Angle(body.right,target.position-body.position)>90f) side = "left"; else side = "right";
   
    }

}

If angle between right Vector of body transform and target direction is greater than 90, it is left side.

2 Likes

Simpler yet, takes same arguments as Vector3.Angle:

    public static float CalculateAngle(Vector3 from, Vector3 to) {

        return Quaternion.FromToRotation(Vector3.up, to - from).eulerAngles.z;

    }

Courtesy of Getting 360 angle between two 3d vectors for Unity3D. · GitHub

1 Like

For 2D:

    /// <summary>Calculates angle between 2 vectors. Angle increases counter-clockwise.</summary>
    /// <param name="p1">1st point.</param>
    /// <param name="p2">2nd point.</param>
    /// <param name="o">Starting position of vectors.</param>
    /// <returns>Angle between 0° and 360°.</returns>
    public static float Angle360(Vector2 p1, Vector2 p2, Vector2 o = default(Vector2))
    {
        Vector2 v1, v2;
        if (o == default(Vector2))
        {
            v1 = p1.normalized;
            v2 = p2.normalized;
        }
        else
        {
            v1 = (p1 - o).normalized;
            v2 = (p2 - o).normalized;
        }
        float angle = Vector2.Angle(v1, v2);
        return Mathf.Sign(Vector3.Cross(v1, v2).z) < 0 ? (360 - angle) % 360 : angle;
    }
1 Like

Useful for 3D/2D(z value equal to 0):

    float angle360(Vector3 from, Vector3 to, Vector3 right)
    {
        float angle = Vector3.Angle(from, to);
        return (Vector3.Angle(right, to) > 90f) ? 360f - angle : angle;             
    }

To calculate 360 degree, you need a perspective. For perspective, I use right vector instead up vector.

4 Likes

LOL you guys take so many time on this. Indeed I just found a fast way

        angle = Vector3.Angle((target.transform.position - myObject.transform.position), myObject.transform.forward);
        float angle2 = Vector3.Angle((target.transform.position - myObject.transform.position), myObject.transform.right);

        if (angle2 > 90)
        {
            angle = 360 - angle;
        }
4 Likes
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FindDirections : MonoBehaviour
{
    [SerializeField] GameObject Target;

    [SerializeField] Vector2 Fwd_MinMax = new Vector2(50f, 140f);
    [SerializeField] Vector2 Side_MinMax = new Vector2(50f, 140f);

    enum MyEnum
    {
        Bow,
        Left,
        Right,
        Stern,
        UnAssigned
    }
    MyEnum targetDirection = MyEnum.UnAssigned;

    private void Update()
    {
        WhereIsTarget();
    }

    void WhereIsTarget()
    {
        Collider myCollider = transform.GetComponent<Collider>();
        Vector2 myColliderCenter = new Vector2(myCollider.bounds.center.x, myCollider.bounds.center.z);

        Vector2 target_asV2 = new Vector2(Target.transform.position.x, Target.transform.position.z);
        float angle = Vector2.Angle(Vector2.up, target_asV2 - myColliderCenter);

        if(angle <= Fwd_MinMax.x)
        {
            Debug.Log("Target in front");
            targetDirection = MyEnum.Bow;
        }

        if(angle > Fwd_MinMax.x && angle < Fwd_MinMax.y)
        {
            float angle_XAxis = Vector2.Angle(Vector2.right, target_asV2 - myColliderCenter);

            if(angle_XAxis <= Side_MinMax.x)
            {
                Debug.Log("Target right");
                targetDirection = MyEnum.Right;
            }
            if(angle_XAxis >= Side_MinMax.y)
            {
                Debug.Log("Target left");
                targetDirection = MyEnum.Left;
            }
        }

        if(angle >= Fwd_MinMax.y)
        {
            Debug.Log("Target behind");
            targetDirection = MyEnum.Stern;
        }
    }

    private void OnDrawGizmos()
    {
        if(Target != null)
        {
            Vector2 tempColliderCenter = new Vector2(transform.GetComponent<Collider>().bounds.center.x, transform.GetComponent<Collider>().bounds.center.z);
            Vector3 tempPos = new Vector3(tempColliderCenter.x, 0, tempColliderCenter.y);

            Gizmos.DrawSphere(tempPos, .3f);
            Gizmos.DrawLine(tempPos, Target.transform.position);
        }
    }
}
2 Likes

Vector3.Angle gives 0-180 degrees

However, Vector3.SignedAngle gives you -180 to 180,
so you can get the angle to 360

if you add 360 to any negative angle values.

1 Like

This code will give you 0 to 360 angle

float angle = Vector2.SignedAngle(vec0, vec1);
if (angle < 0)
    angle += 360;
1 Like