There are a few issues in this code, some of which could be the cause for it to perform poorly.
- Directly compares sets of numbers
- Indexing with
.
then checking for nil
- Lacks variables for intermediate operations
-
CFrame.new
instead of CFrame.lookAt
Fixing this step by step, we first remove the comparison. Noting that you should never directly compare Vector3
s or CFrame
s unless they are sentinel values, because number arithmetic can cause seemingly equal numbers to not come up as such. This also removes our unnecessary allocation, because CFrame.new
creates a new value every time.
Also, it’s unlikely that character velocity will always equal exactly 0
even when apparently standing still, so I’d replace that check with comparison against a small number of your choice. In this case, I used 0.1
.
local cam = workspace.CurrentCamera
local plr = game:GetService("Players").LocalPlayer
game:GetService("RunService").RenderStepped:connect(function()
local character = plr.Character
if character then
local root = plr.Character.HumanoidRootPart
if root and root.Velocity.magnitude <= 0.1 then
root.CFrame = root.CFrame:lerp(CFrame.new(root.CFrame.p,root.CFrame.p+Vector3.new(cam.CFrame.lookVector.X,0,cam.CFrame.lookVector.Z)),0.1)
end
end
end)
Next we address that Character.HumanoidRootPart
can fail as an error. Since Player.Character
is a property, it returns nil
if it doesn’t exist, so that check is fine. However, Character.HumanoidRootPart
is an instance look up, which hard errors when it fails. In this case, FindFirstChild
is what you’re looking for.
local cam = workspace.CurrentCamera
local plr = game:GetService("Players").LocalPlayer
game:GetService("RunService").RenderStepped:connect(function()
local character = plr.Character
if character then
local root = plr.Character:FindFirstChild("HumanoidRootPart")
if root and root.Velocity.magnitude <= 0.1 then
root.CFrame = root.CFrame:lerp(CFrame.new(root.CFrame.p,root.CFrame.p+Vector3.new(cam.CFrame.lookVector.X,0,cam.CFrame.lookVector.Z)),0.1)
end
end
end)
Now to break up the intermediate operations and use CFrame.lookAt
. We also use vector multiplication to zero out the Y axis.
local cam = workspace.CurrentCamera
local plr = game:GetService("Players").LocalPlayer
game:GetService("RunService").RenderStepped:connect(function()
local character = plr.Character
if character then
local root = plr.Character:FindFirstChild("HumanoidRootPart")
if root and root.Velocity.magnitude <= 0.1 then
local direction = cam.CFrame.lookVector * Vector3.new(1, 0, 1)
local desired = CFrame.lookAt(root.CFrame.p, root.CFrame.p + direction)
root.CFrame = root.CFrame:lerp(desired, 0.1)
end
end
end)
As a final step, let’s also update the functions and fields used to be more up to date. .p
and :lerp
, for example, are deprecated.
local cam = workspace.CurrentCamera
local plr = game:GetService("Players").LocalPlayer
game:GetService("RunService").RenderStepped:Connect(function()
local character = plr.Character
if character then
local root = plr.Character:FindFirstChild("HumanoidRootPart")
if root and root.Velocity.Magnitude <= 0.1 then
local direction = cam.CFrame.LookVector * Vector3.new(1, 0, 1)
local desired = CFrame.lookAt(root.Position, root.Position + direction)
root.CFrame = root.CFrame:Lerp(desired, 0.1)
end
end
end)
This should perform better as you’ve avoided a lot of unneeded operations, but your bottleneck could be in an entirely different piece of code too. You’ll have to test and see.