Tips on improving optimisation for this movement script?

Hi there!

There is a lot more code behind this than what is shown here, if you need anything more I’ll be happy to provide it.

What I’ve made here is a movement script that attempts to emulate Source engine (HL2, CS:GO) movement as best as possible in Roblox. I’ve modified it heavily (possibly making it worse in places?) from a script originally made by SnarlyZoo (link: New Bunny Hoppy Movement Code). I’ve added in crouching and sprinting, as well as reorganised it and removed the bunnyhopping feature entirely as it’s not something I need.

So this code works extremely well for me (which surprises me, as it’s the most complex thing I’ve ever tried up to this point). My only problem is trying to improve optimisation, as I can see how the number of if loops and coroutines may be a problem not just for those on potato computers but on higher-end computers as more and more gets added to this game. This is only part of the code, but it’s the part which I think needs the most work.

Background information that may help:

  • This is in StarterPlayerScripts named as ControlScript
  • This is first person only, so I have made a custom player character with only 2 parts: a humanoid root part (which is being tweened in crouchEnter and crouchExit) and a part which checks whether the player is grounded
  • Some stuff in here may be remnants of the original code that is actually redundant and I haven’t realised yet
  • I’m not the best at scripting. I wouldn’t say I’m a complete beginner but I’m certainly not advanced.
----- ACTIONS -----

local function onFwd(actionName, inputState)
	
	if inputState == Enum.UserInputState.Begin then
		
		Cmd.fwdMove = true
		Cmd.lastFwd = true
		
		
	elseif inputState == Enum.UserInputState.End then
		
		Cmd.fwdMove = false
				
	end
	
end 



local function onBack(actionName, inputState)
	
	if inputState == Enum.UserInputState.Begin then
		
		Cmd.backMove = true
		Cmd.lastFwd = false
				

	elseif inputState == Enum.UserInputState.End then
		
		Cmd.backMove = false
				
	end
	
end



local function onLeft(actionName, inputState)
	
	if inputState == Enum.UserInputState.Begin then
		
		Cmd.leftMove = true
		Cmd.lastLeft = true
			
		
	elseif inputState == Enum.UserInputState.End then
		
		Cmd.leftMove = false
		

	end
	
end



local function onRight(actionName, inputState)
	
	if inputState == Enum.UserInputState.Begin then
		
		Cmd.rightMove = true
		Cmd.lastLeft = false
		
		
		
	elseif inputState == Enum.UserInputState.End then
		
		Cmd.rightMove = false
				
	end
	
end



local function onSprint(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.sprint = true


	elseif inputState == Enum.UserInputState.End then

		Cmd.sprint = false

	end

end 



local function onJump(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.jump = true
		Cmd.lastCrouch = false



	elseif inputState == Enum.UserInputState.End then

		Cmd.jump = false

	end

end 



local function onCrouch(actionName, inputState)
	
	if inputState == Enum.UserInputState.Begin then
		
		Cmd.crouch = true
		Cmd.lastCrouch = true
		
		
	elseif inputState == Enum.UserInputState.End then
		
		Cmd.crouch = false
		crouchExit:Play()
		crouching = false
		
	end
	
end

----- BINDINGS -----

ContextActionService:BindAction("Forward", onFwd, false, Enum.KeyCode.W)
ContextActionService:BindAction("Left", onLeft, false, Enum.KeyCode.A)
ContextActionService:BindAction("Back", onBack, false, Enum.KeyCode.S)
ContextActionService:BindAction("Right", onRight, false, Enum.KeyCode.D)
ContextActionService:BindAction("Jump", onJump, false, Enum.KeyCode.Space)
ContextActionService:BindAction("Crouch", onCrouch, false, Enum.KeyCode.LeftControl)
ContextActionService:BindAction("Sprint", onSprint, false, Enum.KeyCode.LeftShift)



local function Update(deltaTime)
	
	grounded = CheckGround()
	
	if not humanoidRootPart then return end
	
	if Cmd.leftMove and Cmd.rightMove then

		if Cmd.lastLeft then sideMove = -1 else sideMove = 1 end

	else

		if Cmd.leftMove then sideMove = -1 elseif Cmd.rightMove then sideMove = 1 else sideMove = 0 end

	end

	if Cmd.fwdMove and Cmd.backMove then

		if Cmd.lastFwd then forwardMove = -1 else forwardMove = 1 end

	else

		if Cmd.fwdMove then forwardMove = -1 elseif Cmd.backMove then forwardMove = 1 else forwardMove = 0 end

	end
	
	if Cmd.jump and canJump and grounded then

		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		canJump = false

	end
	
	if Cmd.crouch then 
		
		crouchEnter:Play()
		crouching = true
		
	else 
		
		crouchExit:Play()
		crouching = false
		
	end
	
	
	local speedChange = coroutine.create(function()
		
		if grounded == false then
			
			sideMove = 0
			
		else 
			
			sideMove = sideMove
			
		end
			
		if Cmd.crouch then 

			moveSpeed = 6.333

		elseif Cmd.sprint then 

			moveSpeed = 30

		else 

			moveSpeed = 15

		end

	end)


	local jumpLimit = coroutine.create(function()

		if Cmd.jump and canJump == false and grounded then
			
			local begin = tick()

			while tick() - begin < jumpBuffer do

				RunService.Heartbeat:Wait()

			end

			canJump = true

		end

	end)
	
	

	local cameraCrouch = coroutine.create(function()
		
		if Cmd.jump then 
			
			camera.CFrame = camera.CFrame
				
		end

		if Cmd.crouch and Cmd.jump == false then

			cameraCrouchEnter:Play()

		elseif Cmd.crouch == false and crouching then

			cameraCrouchExit:Play()
			crouching = false

		end

	end)
	
	
    
	coroutine.resume(jumpLimit)
	coroutine.resume(cameraCrouch)
	coroutine.resume(speedChange)


	if grounded then

		GroundMove()

	else

		AirMove()

	end


	local newDir = Vector3.new()
	if playerVel ~= Vector3.new(0, 0, 0) then newDir = playerVel.Unit end

	local newSpeed = playerVel.Magnitude
	newSpeed = math.clamp(newSpeed, 0, maxSpeed)

	humanoid.WalkSpeed = newSpeed
	humanoid:Move(newDir, false)

end

RunService.RenderStepped:Connect(Update)

The main problem I have with this is where to start. I understand that running so many if loops is inefficient, same as the 3 coroutines, but I don’t really know where to start with it. I want to make this as efficient as possible but without losing the good results I already had. I’ve tried some stuff already but it’s just messed up the camera or broken it entirely.

Also I’m pretty sure that jumpLimit is irrelevant, ignore it. I’ll either make it useful or remove it in the future with some final things I want to modify.

If it’s not about optimisation, please still say it! I’m always open to improving what I’ve written in any way possible.
Thanks!

2 Likes