Optimize local tool aim script

I have created this localscript called localgunscript that makes the player aim a tool to wherever the mouse is pointing. It works by setting the c0 of the waist motor6d, and runs every heartbeat. However, as seen in the video, it has a high script performance, ranging from 0.2% to 1.1% How do I optimize the code so it becomes lower? The code is below the video:

local Tool = script.Parent
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local origc0 = nil

Tool.Equipped:Connect(function()
	if not game.UserInputService.VREnabled then
		local char = Player.Character
		local Root = char.HumanoidRootPart

		if char:FindFirstChild('UpperTorso') then
			origc0 = char.UpperTorso:WaitForChild('Waist').C0
		end

		renderconnec = game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
			if char:FindFirstChild('Humanoid') and char.Humanoid.Health > 0 then
				local lerpSpeed = 10/(1/deltaTime)
				local cframe = CFrame.new(char.UpperTorso.Position, Mouse.Hit.Position)
				if char.Humanoid.Sit == false then
					local RootPos, MousePos = Root.Position, Mouse.Hit.Position
					Root.CFrame = CFrame.new(RootPos, Vector3.new(MousePos.X, RootPos.Y, MousePos.Z))
				end

				if char:FindFirstChild('UpperTorso') then
					local x,y,z = (CFrame.new(char.UpperTorso.Waist.C0.Position) * char.UpperTorso.CFrame:toObjectSpace(cframe)):ToEulerAnglesXYZ()
					local cf2 = CFrame.new(char.UpperTorso.Waist.C0.Position) * CFrame.Angles(math.clamp(x,math.rad(-60),math.rad(90)),0,0) 
					char.UpperTorso.Waist.C0 = char.UpperTorso.Waist.C0:Lerp(cf2,lerpSpeed)
				end	
			else
				renderconnec:Disconnect()
			end	
		end)
	end	
end)

Tool.Unequipped:Connect(function()
	if not game.UserInputService.VREnabled then
		local char = Player.Character

		if renderconnec then
			renderconnec:Disconnect()
		end

		if char and char:FindFirstChild("UpperTorso") then
			char.UpperTorso.Waist.C0 = origc0
		end
	end	
end)

I did some testing, and found out that Runservice:BindToRenderStep() got the lowest script activity, with 0.391 at its peak. Second was Runservice.postsimulation with a peak of 0.45. Still, if people know other ways to optimize this script then do tell!

Don’t use findfirstchild so much, you are pretty much calling a function that iterates through the entire character, you can just existence check it and then run the code normally

what do you mean existence check? People in my game can lose their humanoids, so i would need to check whether it exists right? Is there a different way to check whether a player has an uppertorso and humanoid? Or should I just let it error if they are not existent?

At the start of your script you can see if the uppertorso/humanoid exists, and if it doesnt you can just disconnect the loop and return

But then what if a player loses their uppertorso or humanoid when the loop is running? it would error without the findfirstchilds right?

No cause th escript would see that it doesn’t exist at the very start and it would return it
Here is the code sample:

local Tool = script.Parent
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local origc0 = nil

Tool.Equipped:Connect(function()
	if not game.UserInputService.VREnabled then
		local char = Player.Character
		local Root = char.HumanoidRootPart
		local UpperTorso = char.UpperTorso
		local Waist = UpperTorso.Waist
		local Humanoid = char.Humanoid

		origc0 = Waist.C0

		renderconnec = game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
                if not char:FindFirstChild("UpperTorso") or not char:FindFirstChild("Humanoid") then warn("No humanoid or upper torso. Aborting!") renderconnec:Disconnect() renderconnec = nil return end
			if Humanoid.Health > 0 then
				local lerpSpeed = 10/(1/deltaTime)
				local cframe = CFrame.new(UpperTorso.Position, Mouse.Hit.Position)

				if not Humanoid.Sit then
					local RootPos, MousePos = Root.Position, Mouse.Hit.Position
					Root.CFrame = CFrame.new(RootPos, Vector3.new(MousePos.X, RootPos.Y, MousePos.Z))
				end

				local x, y, z = (CFrame.new(Waist.C0.Position) * UpperTorso.CFrame:toObjectSpace(cframe)):ToEulerAnglesXYZ()
				local cf2 = CFrame.new(Waist.C0.Position) * CFrame.Angles(math.clamp(x, math.rad(-60), math.rad(90)), 0, 0) 
				Waist.C0 = Waist.C0:Lerp(cf2, lerpSpeed)
			else
				renderconnec:Disconnect()
			end	
		end)
	end	
end)

Tool.Unequipped:Connect(function()
	if not game.UserInputService.VREnabled then
		local char = Player.Character

		if renderconnec then
			renderconnec:Disconnect()
		end

		local UpperTorso = char.UpperTorso
		local Waist = UpperTorso.Waist
		Waist.C0 = origc0
	end	
end)

1 Like

I tested this edited version, but it somehow increased the script activity? before with postsimulation it was 2.0% and with ur optimization it is 2.6%?

  1. use vector library instead of vector3 its faster.

2)Dont use findfirstchild inside the tick function. every findfirstchild, findfirstchildof etc is 8 times slower.

1 Like

Implementing your tips also helped reduce the activity, thanks. my equipped function now looks like this:

Tool.Equipped:Connect(function()
if not game.UserInputService.VREnabled then
		local char = Player.Character
		local Root = char:FindFirstChild('HumanoidRootPart')
		local UpperTorso = char:FindFirstChild('UpperTorso')
		local Waist = UpperTorso:FindFirstChild('Waist')
		local Humanoid = char:FindFirstChild('Humanoid')
		
		if char and UpperTorso and Waist and Humanoid and Root then		
			origc0 = Waist.C0

			renderconnec = game:GetService('RunService').PostSimulation:Connect(function(deltaTime)
				if Humanoid.Health > 0 then
					local lerpSpeed = 10/(1/deltaTime)
					local cframe = CFrame.new(UpperTorso.Position, Mouse.Hit.Position)
					if not Humanoid.Sit then
						local RootPos, MousePos = Root.Position, Mouse.Hit.Position
						Root.CFrame = CFrame.new(RootPos, vector.create(MousePos.X, RootPos.Y, MousePos.Z))
					end

					local x,y,z = (CFrame.new(Waist.C0.Position) * UpperTorso.CFrame:toObjectSpace(cframe)):ToEulerAnglesXYZ()
					local cf2 = CFrame.new(Waist.C0.Position) * CFrame.Angles(math.clamp(x,math.rad(-60),math.rad(90)),0,0) 
					Waist.C0 = Waist.C0:Lerp(cf2,lerpSpeed)
				else
					renderconnec:Disconnect()
				end	
			end)
		end	
	end	
end)
1 Like

a new issue might happen if you unequip the tool while alive and equip it again, since that doesnt disconnect when the humanoid is unequipped. that will cause major issue if player equips and unequips tools since it will call the same function multiple times

No that is fine, if you look at the unequipped function in the original script, you can see that it checks whether the connection exists and disconnects it if it does.