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)
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.
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.