This post is updated
Updated on 04/03/2025 please scroll to the bottom!
Following method it outdated:
It’s funny that lookAt functions typically employ cross and dot products rather than trigonometric functions. But because Roblox has limited matrix support, we are forced (kind of) to use spherical coordinates. Though it is a little disappointing, it’s okay. This strategy is more trigonometric.
function LookAt(Eye: Vector3, Target: Vector3)
local dx = Eye.X - Target.X
local dy = Eye.Y - Target.Y
local dz = Eye.Z - Target.Z
local r = math.sqrt(dx^2 + dy^2 + dz^2)
local theta = math.acos(dz / r)
local phi = math.atan2(dy, dx)
local Hat_r = Vector3.new(
-math.sin(theta) * math.cos(phi),
-math.sin(theta) * math.sin(phi),
-math.cos(theta)
)
local Hat_theta = Vector3.new(
-math.sin(phi),
math.cos(phi),
0
)
local Hat_phi = Vector3.new(
math.cos(theta) * math.cos(phi),
math.cos(theta) * math.sin(phi),
-math.sin(theta) -- Adjusted sign
)
return CFrame.fromMatrix(
Eye,
Hat_theta,
-Hat_phi,
-Hat_r
)
end
If you noticed me reacting, it’s because I tried the traditional approach but it wasn’t very effective. When the eye part’s x and z coordinates matched, my part was being expelled into nothingness. Anyway, here are some resources you may look at if you want to learn more about spherical coordinates or the conventional method of doing it.
Please ask me if you have questions or issues. I’m not bothered.
Spherical coordinates (The “magic”)
https://mathinsight.org/spherical_coordinates
Conventional method for lookAt functions
This link is removed since it’s dead. Please scroll further to find the article in the updated section. Better safe than sorry
Footage of the old method
I selected the part in the explorer and pushed F to get to it.
Footage of the new method (Spherical coordinates are very cool don’t you agree?
)
Updated method
Whats-up! I don’t know what I was on when I was writing this article but some information provided is plain wrong, I’d like to apologize in advance. Albeit works it’s far from optimal nor informative and I’m here to address some of these previous mistakes:
Here’s the updated function:
function lookAt (at: Vector3, lookAt: Vector3, up: Vector3?): CFrame
local zaxis = (lookAt - at).Unit
local xaxis = zaxis:Cross(up or Vector3.yAxis).Unit
local yaxis = xaxis:Cross(zaxis)
zaxis = -zaxis
return CFrame.fromMatrix(
at,
xaxis,
yaxis,
zaxis
)
end
-- Example usage
local eye: Part = workspace.eye -- at
local subject: Part = workspace.subject -- lookAt
eye.CFrame = lookAt(eye.Position, subject.Position)
Alright so first of all Roblox doesn’t have limited matrix support you just have to implement it yourself whether that’d be a table or a custom method if you want to use them. That said spherical coordinates are still very useful and I’d still recommend learning about them since they’re pretty easy to understand. They shine in some use cases but I wouldn’t recommend using them for a lookAt function.
I also recommend adding this statement at the start of the lookAt function. Otherwise when the two positions are the same your part disappears:
function lookAt (at: Vector3, lookAt: Vector3, up: Vector3?): CFrame
-- TODO There may be some unexpected behavior so make sure to test this function
if at.X == lookAt.X and at.Z == lookAt.Z then
if at.Y >= lookAt.Y then
return CFrame.fromMatrix(at, -Vector3.xAxis, Vector3.zAxis, Vector3.yAxis)
elseif at.Y <= lookAt.Y then
return CFrame.fromMatrix(at, Vector3.xAxis, Vector3.zAxis, -Vector3.yAxis)
end
end
-- ...
About the article
The old article about this is dead and for some reason clicking said link references a website where you can purchase a book of some sort? I’m not quite sure myself but I deleted the link just to be safe. To find the article just search up:
Geert Arien - Breakdown of the LookAt function in OpenGL
The code provided above is just a translation of the function to luau that was taken from the article mentioned above. If you want to know how it works you can visit said article that’s all, have a wonderful rest of your day/night!