Best way to get the angle between two vectors?

For my NPC system, I’m looking to have the NPC rotate their head to look at you (obviously with rotation limits). The desired effect will look something like this:

To rotate the head, I use tween service. In the example above, I did:

CFrame = head.CFrame * CFrame.fromEulerAnglesXYZ(0,math.rad(45))

My question is, what’s the easiest way to get the angle required for the NPC to look at the player?

CFrame = head.CFrame * CFrame.fromEulerAnglesXYZ(0,math.rad(θ))

I could use trigonometry, I’m just curious to know if there’s an easier way to obtain the desired angle?

5 Likes

This should work.

CFrame.new ( Vector3 pos, Vector3 lookAt )
head.CFrame = CFrame.new(head.Position,Vector3.new(playerHumanoidRootPart.Position.x,head.Position.y,playerHumanoidRootPart.Position.z))

17 Likes

SebastianAurum provided the best way of solving OPs actual problem, but in case someone in the future really does need to find such an angle, here’s how you can do that:

First of all, you can’t find an angle between two points. It just doesn’t make any sense, because an angle needs at least three points to be properly defined: a point each on the legs of the angle, and one for the point/knee of the angle. In OPs case, it looks like they’re really asking for the angle between two vectors, which does make sense (the vector from NPCs head to players head, and vector of direction of NPCs head).

The angle between two vectors a and b is

math.acos( a:Dot(b)/(a.Magnitude * b.Magnitude) )

We often deal with the special case where both vectors are unit vectors (i.e. their magnitude is 1), in which case this slightly simpler expression that you might see being used elsewhere works as well:

math.acos( a:Dot(b) )

This technique only returns angles in the interval [0; pi] because two vectors can never point more away from each other than directly away from each other, which can always be achieved by a rotation of pi radians = 180 degrees. This means the angle you get in this way has no direction (it’s never negative), so if you need that direction you’ll need a different technique:

First of all, this method works best with a CFrame and a vector (could also use 3 vectors), and then gets the angle between the CFrame’s lookVector and the given vector. Because the CFrame has an upVector as well, we can work with the direction:

local projectedVector = cframe:VectorToObjectSpace(vector) * Vector3.new(1, 0, 1)
local angle = math.atan2(projectedVector.Z, projectedVector.X)

This works by projecting the vector onto a 2D plane perpendicular to the upVector of the cframe, and converted to a representation that is in relation to the cframe. It then uses atan2 to calculate the angle between the lookVector of the cframe and the vector in that plane.


EDIT: :rotating_light: Please see the following comments for a bit of clarification :rotating_light:

117 Likes

Nice explanation, thanks for the clarification.

5 Likes

You are a lifesaver <3. This is going to be perfect for the tank I am creating.

8 Likes

Found an error in the OP post

This should be using PointToObjectSpace instead, as this transforms a vector into object space.
VectorToObjectSpace doesn’t do what the name implies, the whole documentation for these methods is really ugly.

The fix is:

local projectedVector = script.Parent.Base.CFrame:PointToObjectSpace(pos) * Vector3.new(1, 0, 1)
5 Likes

Thanks, that’s a great clarification! In the case where vector is a point in 3D space, @AbstractAlex 's response makes more sense than what I wrote. If vector is the difference between a point in 3D space and the position component of a CFrame, then it is the direction to the point from the CFrame.

VectorToObjectSpace and VectorToWorldSpace mostly make sense when working with vectors that are in the CFrame’s local space. In the former example that is not the case, in the latter it is.

I’ve updated my comment to make sure people see this :slight_smile: If there are any other corrections or clarifications please let me know, at > 100 :heart:s it’s probably worth it to make the comment as good as possible

2 Likes