local player = game.Players.LocalPlayer
repeat wait() until player:GetMouse() and player.Character
local mouse = player:GetMouse()
local character = player.Character
local atan2, pi, dir, torso, torsoPos = math.atan2, math.pi
game:GetService("RunService").RenderStepped:connect(function()
torso = character:FindFirstChild("Torso")
if torso then
torsoPos = torso.Position
dir = (mouse.Hit.p - torsoPos).unit
torso.CFrame = CFrame.new(torsoPos) * CFrame.Angles(0, atan2(dir.X, dir.Z) + pi, 0)
end
end)
May someone explain how each part in this script works - line including math.atan2?
1 answer
local atan2, pi, dir, torso, torsoPos = math.atan2, math.pi
This establishes atan2 as a shorter name of math.atan2 and pi as a shorter name for math.pi.
It also defines dir, torso, and torsoPos as local variables, which is a bad thing -- variables should always be defined in the narrowest scope possible -- they should be defined where they are used, and not before.
game:GetService("RunService").RenderStepped:connect(function()
Do this function, constantly (every time the frame is rendered, which will be approximately 60 times a second)
torso = character:FindFirstChild("Torso")
Get the Torso in the player's character (which may not exist because they may be dead or not yet spawned)
if torso then
If the torso does exist (they are alive and spawned)
torsoPos = torso.Position
save the position of the torso to torsoPos. This just shortens the name from torso.Position.
dir = (mouse.Hit.p - torsoPos).unit
mouse.Hit.p is the point in space where the mouse is currently hovering.
Subtracting one vector from another gets the displacement between the two -- it's the vector "between" them. Thus B - A is in the direction of B from A. Thus, (mouse.Hit.p - torsoPos) is the way pointing towards the mouse from the torso's position.
You take the .unit because a displacement includes distance, but for directions, we don't want distance usually. In this code the .unit is unnecessary
torso.CFrame =
Set the torso's CFrame -- we're going to make it point at the mouse.
CFrame.new(torsoPos)
this is a CFrame with the default orientation at the same position as Torso. This means it will reset how the torso is rotated, but keep it in the same place.
* CFrame.Angles(0, atan2(dir.X, dir.Z) + pi, 0)
Multiplying by a CFrame rotates by that amount.
atan2(y, x) gives you the angle between (0, 0)--(1, 0) (the x-axis) and (x, y). Thus, this is the right amount to spin the default CFrame in order to get the torso to face towards the mouse. We have to add pi probably because this is backward.
Rotations are measured in radians, so pi is half of a full revolution.
Slightly Simpler
There's a slightly simpler way to accomplish this.
local flat = Vector3.new(1, 0, 1) -- Something we can use to ignore changes in Y
local toMouse = (mouse.Hit.p - torso.Position)
local toMouseFlat = toMouse * flat
torso.CFrame = CFrame.new(torso.Position, torso.Position + toMouseFlat)
toMouse * flat gets us the direction towards the mouse, but ignores y -- that way the torso only turns side to side, but doesn't look up and down.
CFrame.new(x, y) gives you a CFrame at the position of x facing towards y.
We want to face towards the mouse, but have it at the same y as the torso. torso.Position + toMouseFlat is exactly that:
torso + toMouseFlat = (tx, ty, tz) + (1, 0, 1) * ((mx, my, mz) - (tx, ty, tz)) = (tx, ty, tz) + (mx, 0, mz) - (tx, 0, tz) = (mx, ty, mz).
We could also explicitly construct this:
local p = torso.Position
local m = mouse.Hit.p
torso.CFrame = CFrame.new(p, Vector3.new(m.x, p.y, m.z))
EDIT: After thinking about it, the final way is much clearer.
The method using flat avoids using .x and .y and .z, which usually is cleaner, but in this case it just makes it messy. Further explanation of it:
The flat variable essentially describes only the horizontal parts of a position. When you multiply vectors, you multiply each component. That means the resulting .x and .z will be the same (since * 1) but the resulting .y will be 0 (since * 0).
That means toMouseFlat is in the direction of the mouse from the torso, but is completely flat. We can then look from torso to torso + toMouseFlat, or, in other words, look in the direction of toMouseFlat from the torso.
1 comment
I have two questions: with the flat varialbe, why is 1 set on the x and z and also can you explain the multiplying of to mouseflat variable.
This discussion is preserved as it was. New answers, comments, and votes are closed.

2 comments
Use codeblock bro
Do you know how trigonometric functions and their inverses work? Were you taught that in school?