Movement script crouch and sprint overlapping even when parameters are placed

I’ve been working on this movement script, and it’s been going well for most of the time that i’ve been working on it, but whenever I press the shift, and control keys at the same time EVEN when I place specific parameters to prevent the keys overlapping, they still somehow overlap and I don’t know why.

If you have a solution to this specific problem, please tell me because I have been confused for a very long time on this issue.

Script:

--services
local uis = game:GetService("UserInputService")
local ts = game:GetService("TweenService")

--player variables
local plrCam = workspace.CurrentCamera
local plr = game.Players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local hrp = char:FindFirstChild("HumanoidRootPart")
local hum = char:FindFirstChild("Humanoid")

--bools
local debounce = false
local isRunning = false
local isCrouching = false

--config/settings (you can fine-tune these variables as much as you want)
local runDebounceTime = 0.25
local walkSpeed = 16
local runSpeed = 30
local crouchSpeed = 5
local crouchChangeSpeed = 0.2
local crouchOffset = Vector3.new(0, -1.5, 0)
local standOffset = Vector3.new(0, 0, 0) -- dont change this pls :)
local fovBase = 70
local fovIncrease = 110
local fovDecrease = 50
local fovChangeTime = 0.2
local runKey = Enum.KeyCode.LeftShift or Enum.KeyCode.RightShift
local crouchKey = Enum.KeyCode.LeftControl or Enum.KeyCode.RightControl

--tween service thingies (the visual part of things)
local fovIncGoal = {}
fovIncGoal.FieldOfView = fovIncrease
local fovBaseGoal = {}
fovBaseGoal.FieldOfView = fovBase
local fovDecGoal = {}
fovDecGoal.FieldOfView = fovDecrease

local camOffsetTweenInfo = TweenInfo.new(crouchChangeSpeed, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut)
local crouchOffsetTween = ts:Create(hum, camOffsetTweenInfo, {CameraOffset = crouchOffset})
local standOffsetTween = ts:Create(hum, camOffsetTweenInfo, {CameraOffset = standOffset})

local fovTweenInfo = TweenInfo.new(fovChangeTime, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 0, false, 0)
local fovIncreaseTween = ts:Create(plrCam, fovTweenInfo, fovIncGoal)
local fovBaseTween = ts:Create(plrCam, fovTweenInfo, fovBaseGoal)
local fovDecreaseTween = ts:Create(plrCam, fovTweenInfo, fovDecGoal)


hum.WalkSpeed = walkSpeed
plrCam.FieldOfView = fovBase

print(hum.WalkSpeed)

--uis stuffs
uis.InputBegan:Connect(function(inpObj, gpe)
	if debounce == true then return end
	
	if inpObj.KeyCode == runKey and isCrouching == false and isRunning == false then
		if hum:GetState() == Enum.HumanoidStateType.Dead then return end
		debounce = true
		isRunning = true
		
		plrCam.CameraType = Enum.CameraType.Scriptable
		fovIncreaseTween:Play()
		plrCam.CameraType = Enum.CameraType.Custom
		
		hum.WalkSpeed = runSpeed
		task.wait(runDebounceTime)
		debounce = false
	end
	
	if inpObj.KeyCode == crouchKey and isRunning == false and isCrouching == false then
		if hum:GetState() == Enum.HumanoidStateType.Dead then return end
		
		isCrouching = true
		
		plrCam.CameraType = Enum.CameraType.Scriptable
		fovDecreaseTween:Play()
		plrCam.CameraType = Enum.CameraType.Custom
		
		crouchOffsetTween:Play()
		hum.WalkSpeed = crouchSpeed
	end
end)

uis.InputEnded:Connect(function(inpObj, gpe)	
	if inpObj.KeyCode == runKey and isRunning == true and isCrouching == false then
		if hum:GetState() == Enum.HumanoidStateType.Dead then return end
		isRunning = false
		
		plrCam.CameraType = Enum.CameraType.Scriptable
		fovBaseTween:Play()
		plrCam.CameraType = Enum.CameraType.Custom
		
		hum.WalkSpeed = walkSpeed
	end
	
	if inpObj.KeyCode == crouchKey and isCrouching == true and isRunning == false then
		if hum:GetState() == Enum.HumanoidStateType.Dead then return end
		if isRunning == true then return end
		isCrouching = false
		
		plrCam.CameraType = Enum.CameraType.Scriptable
		fovBaseTween:Play()
		plrCam.CameraType = Enum.CameraType.Custom
		
		standOffsetTween:Play()
		hum.WalkSpeed = walkSpeed
	end
end)

again, if you have any solution to this issue, let me know.

The biggest mistake is probably this line:

local runKey = Enum.KeyCode.LeftShift or Enum.KeyCode.RightShift

In Lua, or doesn’t work how you think, it just returns LeftShift always. Same problem with your crouch key.

Quick fixes:

  1. Fix key detection:
-- Replace your key variables with arrays
local runKeys = {Enum.KeyCode.LeftShift, Enum.KeyCode.RightShift}
local crouchKeys = {Enum.KeyCode.LeftControl, Enum.KeyCode.RightControl}

-- Then check like this in your input functions:
local function isRunKey(keyCode)
    return keyCode == Enum.KeyCode.LeftShift or keyCode == Enum.KeyCode.RightShift
end
  1. Move the wait outside InputBegan:
-- Instead of task.wait() inside InputBegan, do this:
if inpObj.KeyCode == runKey and isCrouching == false and isRunning == false then
    -- ... your run code ...
    
    task.spawn(function()
        task.wait(runDebounceTime) 
        debounce = false
    end)
end
  1. Add debounce for crouch too - right now only running has debounce, so crouch can interrupt running.

The blocking task.wait() and incorrect key detection are what’s causing your overlap issues. These changes should fix it without needing to rewrite everything. Hope it helps you <3