Get the angle between two vectors on different axis

whew, long title. i have two vectors and i want to know the angles between them on X and Y separately, all the methods ive tried get both.

Project both vectors on to a plane whose normal is the axis you are measuring angle around. Then you can get an included angle using the usual acos(a:Dot(b)) method. If you need an angle from around another vector (like a compass angle), then your plane also needs a tangent vector, which you can just pick however you want.

how do i make a plane? i tried multiplying both vectors by vector3.new(1,0,1) but it still gave me both angles combined

See Vector3 Angle function. That will give you the angle between two vectors. Optionally, you can supply the axis about which to measure them.

3 Likes

i think i know how i could use it but do you have an example for my case?

There is a example:

(Vector3.new(10, 0, 10) - Vector3.new(0, 10, 5)).Unit -- 0.6666666865348816, -0.6666666865348816, 0.333333343267440

Hope that this helped.

1 Like

It measures the angle between two vectors in their normalized form. In other words, it treats the vectors like directions, not positions. (e.g. cframe.UpVector is a vector that describes the upwards direction for that CFrame.)

For example, let’s find the angle between Vector3.yAxis (0, 1, 0) and Vector3.xAxis (1, 0, 0):

local a = Vector3.yAxis
local b = Vector3.xAxis
local angle = a:Angle(b)
print(math.deg(angle))
-- > 90

So there’s a 90 degree measurement from a to b. This essentially finds the shortest angle between the two vectors. The actual math behind this looks something like this (not necessarily optimized):

local function Angle(a: Vector3, b: Vector3): number
   if (a - b).Magnitude < 1e-15 then return 0 end

   local dot = math.clamp(a:Dot(b), -1, 1)
   return math.acos(dot)
end

However, this will always give you a positive angle. So if you did a:Angle(b) or b:Angle(a), they would both be identical. In most circumstances, this is fine. In others, you want to know if it’s a positive or negative angle. But the question is: positive/negative relative to what? That’s where our axis comes into play.

In order to get the sign of the angle (+/-), we need to measure the angle relative to a given axis. Thankfully, the math to do this is quite simple. We can use our current unsigned angle, and then determine if it should be positive or negative by taking the cross product of the two vectors and then finding the dot product between that and the axis vector. If it’s greater than 0, then it’s positive, otherwise negative.

local function AngleSigned(a: Vector3, b: Vector3, axis: Vector3): number
   local unsigned = Angle(a, b)
   local cross = a:Cross(b)
   local dot = axis:Dot(cross)

   return unsigned * math.sign(dot)
end

local a = Vector3.yAxis
local b = Vector3.xAxis
print(math.deg(a:Angle(b, Vector3.zAxis))) --> -90
print(math.deg(b:Angle(a, Vector3.zAxis))) --> 90

I just wrote those out to demonstrate the math. Of course, you don’t need to write all of that, because Roblox now gives us the Angle method on Vector3s.

2 Likes

@sleitnick
What are some practicial uses for a:Angle(b) you might find in the game dev space?

Endless! For example, if you want to find how much a car is drifting, you can find the angle between the direction of the car and its velocity:

local driftAngle = car.CFrame.LookVector:Angle(car.AssemblyLinearVelocity.Unit)

You could also use the dot product in that case, but that’s just a quick example. (FWIW, you’d need to check that the velocity is greater than 0 before using this code)

1 Like

Use Vector3:Dot(Vector3) Or Vector3:Cross(Vector3)

Or (as sleitnick said) Vector3:Angle(Vector3)

will provide further assistance if necessary