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!