- Home /
Given starting Transform and target position, how to find full 360° angle to the target?
I have a GameObject
, and a target's position
(in world coordinates). I want to find the "full" 360° angle to the target (such that "left" gives 90 degrees, "right" gives 270, etc.).
Example: my script is running on a sailing ship, and, given the coordinates of another ship, I want to find the angle to the other ship, starting at my ship's bow and rotating counter-clockwise.
This has been answered piecemeal in other questions, but I wanted to put it all in one place, with a single, fully-functioning code example in the answer.
I was going to self-answer with a working, commented code example. All the other threads on this topic require you to hunt through multiple other threads. Why did you close this?
mainly because it's answered in that question already ("duplicate question" and all), but go ahead if you've got something :)
Fair, sorry - I wasn't clear enough what I was planning. I hope this is a useful addition. Thank you. =)
aw, no prob. us high-falutin' mod types recently got a bit of a bug into our systems, we're all a little trigger-happy on the close button for now, trying to 'straighten up this town', as it were. nice answer, btw. :)
Answer by TheMaster42 · Apr 09, 2013 at 08:44 AM
Given a transform and a target position, you can use the following code to get the "bearing" of your target. The function is also overloaded so you can just provide two GameObjects
.
// Takes a starting object and a target object, and returns the angle to the target 0-359°
function GetBearing(startingObject : GameObject, targetObject : GameObject) : float {
// Call the "real" GetBearing, passing it our object's transform, and the target's position
return GetBearing(startingObject.transform, targetObject.transform.position);
}
// Takes a starting object's Transform, and a target object's position, and returns the angle to the target 0-359°
function GetBearing(startingObjectTransform : Transform, targetPosition : Vector3) : float {
// First, create a Vector3 that is "pointing" from our startingObject to our target.
var vectorToTarget : Vector3 = targetPosition - startingObjectTransform.position;
// Now, discover the angle between our forward "heading", and the vector we just created.
// This only gives a result between 0-180, however... we don't know if the target is to our left or right!
var angleToTarget : float = Vector3.Angle(startingObjectTransform.forward, vectorToTarget);
// AngleDir returns exactly 1 if the target is to the "right" of our forward "heading"
var directionResult = AngleDir(startingObjectTransform.forward, vectorToTarget, startingObjectTransform.up);
// Based on directionResult, decide if angleToTarget was an angle on the "left" or "right" side of our object.
if (directionResult == 1) {
return 360 - angleToTarget;
} else {
return angleToTarget;
}
}
// Given a "forward" heading, a target direction, and an "up" direction, decide if the targetDirection is to our left, right, or front/back.
function AngleDir(forwardVector : Vector3, targetDirection : Vector3, upVector : Vector3) : int {
var perpendicular : Vector3 = Vector3.Cross(forwardVector, targetDirection);
var direction : float = Vector3.Dot(perpendicular, upVector);
if (direction > 0.0) {
return 1;
} else if (direction < 0.0) {
return -1;
} else {
return 0;
}
}
Use like so:
var firingShip : GameObject;
var targetShip : Vector3;
var bearing : float;
// bearing will contain the 360° angle to the targetShip
bearing = GetBearing(firingShip.transform, targetShip);
OR like:
var firingShip : GameObject;
var targetShip : GameObject;
var bearing : float;
// bearing will contain the 360° angle to the targetShip
bearing = GetBearing(firingShip, targetShip);
C# Version without comments (Courtesy Chronos-L):
float GetBearing( GameObject startingObject, GameObject targetObject ) {
return GetBearing( startingObject.transform, targetObject.transform.position );
}
float GetBearing( Transform startTransform, Vector3 targetPosition ) {
Vector3 vectorToTarget = targetPosition - startTransform.position;
float angleToTarget = Vector3.Angle( startTransform.forward, vectorToTarget );
int direction = AngleDir( startTransform.forward, vectorToTarget, startTransform.up );
return ( direction == 1 )? 360f-angleToTarget: angleToTarget;
}
int AngleDir( Vector3 forwardVector, Vector3 targetDirection, Vector3 upVector ) {
float direction = Vector3.Dot( Vector3.Cross( forwardVector, targetDirection ), upVector );
if( direction > 0f ) {
return 1;
}
else if( direction < 0f ) {
return -1;
}
else {
return 0;
}
}
This information was gleaned from the following threads: http://answers.unity3d.com/questions/201996/angle-of-object-relative-to-transform-of-second-ob.html http://forum.unity3d.com/threads/52930-Vector3-Angle()?highlight=AngleDir http://forum.unity3d.com/threads/31420-Left-Right-test-function
It would great if you write another in C#, just in case there are users that claimed that your code did not work when they write it in C#.
One more thing, you should return int
ins$$anonymous$$d of float
for the AngleDir()
. float
are not 100% precise, so there are times that if (directionResult == 1) {
will not be true even though it should be. If you insist on keeping the float, then use if( $$anonymous$$athf.Approximately( directionResult, 1f ) ) {
.
$$anonymous$$y C# is really rusty - I'm sure I'd mess it up. If someone else wrote the code, I'd be happy to include it!
@Chronos-L I was wondering about that! The original example used that... I've fixed it per your suggestion!
Another thing, this is urgent:
//This is incorrect
var angleToTarget : float = Vector3.Angle(startingObjectTransform.forward, targetPosition);
//Correction
var angleToTarget : float = Vector3.Angle(startingObjectTransform.forward, vectorToTarget);
Answer by Chronos-L · Apr 09, 2013 at 09:09 AM
C# Version without comment
float GetBearing( GameObject startingObject, GameObject targetObject ) {
return GetBearing( startingObject.transform, targetObject.transform.position );
}
float GetBearing( Transform startTransform, Vector3 targetPosition ) {
Vector3 vectorToTarget = targetPosition - startTransform.position;
float angleToTarget = Vector3.Angle( startTransform.forward, vectorToTarget );
int direction = AngleDir( startTransform.forward, vectorToTarget, startTransform.up );
return ( direction == 1 )? 360f-angleToTarget: angleToTarget;
}
int AngleDir( Vector3 forwardVector, Vector3 targetDirection, Vector3 upVector ) {
float direction = Vector3.Dot( Vector3.Cross( forwardVector, targetDirection ), upVector );
if( direction > 0f ) {
return 1;
}
else if( direction < 0f ) {
return -1;
}
else {
return 0;
}
}
Answer by Sartoris · Oct 24, 2020 at 09:56 AM
@TheMaster42 Hi, thank you for sharing this code. I'm currently trying to use it in a 2D project to get the bearing between the player's submarine and a ship, but I'm not sure how to adapt it to the 2D environment. Is it enough to replace startTransform.forward with startTransform.right or does it involve normalized values and other changes?