I know this isn’t your question, but I have some solid advice related in the fact you want to crawl with multiple input options (keys, gui buttons, gamepads, etc), and I see the same bad habit all the time. Using your current method of just checking the inputs and doing everything raw right there in the input func seems intuitive at first, until you consider how it will interact with other mechanics in your game.
What do I mean?
How are you going to stop players from jumping while crawling?
How are you going to stop players from sprinting while crawling?
How are you going to stop players from crawling and standing really fast to make their hitbox harder to hit?
How are you going to stop players from standing up in areas that they are not tall enough for and possibly clipping out of bounds?
Where are you going to put the code to check for all this stuff?
My Point:
All of these questions have an obvious answer when you change how the control script functions, and operates with the rest of your code.
My Advice:
From my experience, breaking the code down into seperate functions can make your character actions much more accessible to you as the developer.
Very Basic ControlScript Example:
local UIS = game:GetService("UserInputService")
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
--controls info
local Controls = {
["Crouch"] = {"C","LeftControl","ButtonR3"},
["Sprint"] = {"LeftShift","ButtonL3"},
["Jump"] = {"Space","ButtonA"}
}
--start of state info
local State = "Spawning" --store our current state
local StateTick = 0 --store the tick we last changed states
local TransitionTime = 0 --current cooldown before transitioning to the next state
--run this after a state change to transition the humanoid into it's new state
function UpdateHumanoid()
--special exceptions (e.x there isn't enough room to stand up here! stay in crouch/prone!/prone blocked!)
--play transition tweens (WalkSpeed,HipHeight,Etc)
--update TransitionTime to prevent player changing states too fast
end
local Actions = {}
--set state to Walking
function Actions.Walk()
State = "Walking"
end
--set state to Sprinting
function Actions.Sprint()
State = "Sprinting"
end
--handle jump state logic
function Actions.Jump()
if State == "Walking" then
State = "Jumping"
end
if State == "Jumping" then
if Humanoid.FloorMaterial then
ProcessInputState()--Go Back And Figure out what the heck we should be doing right now!
end
end
if State == "Crouching" then --crouch -> walk
State = "Walking"
end
if State == "Prone" then --prone -> crouch
State = "Crouching"
end
return true--update state tick
end
function Actions.Crouch()
if State == "Sliding" then --sliding -> sliding
--if it's been long enough return to the crouching state?
return false--do not update state tick or we will slide forever lol
end
if State == "Sprinting" then --sprinting -> sliding
State = "Sliding"
else
if State == "Crouching" then --crouching -> prone
State = "Prone"
else
State = "Crouching" --otherwise just crouch
end
end
return true--update state tick
end
function ProcessInputState()--check what buttons we're holding
local Now = tick()--when is right now?
if Now < StateTick+TransitionTime then
return--unable to change action at this time, another action is till being performed
end
for ActionName,InputList in pairs (Controls) do
for Index,KeyCodeName in pairs (InputList) do
if UIS:IsKeyDown(Enum.KeyCode[KeyCodeName]) then
local UpdateStateTick = Actions[ActionName]()--perform action
if UpdateStateTick then
StateTick = Now--last action changed right now
end
UpdateHumanoid()--update our character
end
end
end
end
game["Run Service"].RenderStepped:Connect(ProcessInputState)--check inputs every frame
Result:
- Able to handle state transitions with basic “if this go from this → that” logic
- Able to handle cooldowns to prevent states from changing too quickly
- Able to call actions from anywhere by using Example:Connect(ActionFunction) to make Gui buttons for you actions trivial
your code will be much easier to read, and your character state will be much easier to logic check and set (e.x jumping while prone makes you crouch, jumping again makes you stand up & crouching from a sprinting state results in a sliding state)
When you think about your character controls in this way adding movement mechanics becomes easy and logical, and not a jumbled mess of multiple scripts desperately trying to figure out what they’re allowed to do and not allowed to do/disabling eachother
this is just a basic example that doesn’t actually do anything, the idea I’m trying to present to you is the philosophy of designing movement mechanics for your game that is intuitive to you as the developer, and won’t result in you tearing your hair out later down the line trying to criss-cross all these different mechanics in seperate scripts and have scripts disabling eachother and other nonsense I see developers do on a daily basis.
you can also take it a step farther and use ModuleScripts to keep everything more organized and readable (that’s how Roblox does almost anything, not just their ControlScript)
Hope this advice will help you on your next character control system, or motivates you to rewrite the whole thing entirely. The sky is the limit! Good luck!
tl;dr: doing desired results directly in your input function is bad practice and makes your code harder to work with/read in the long run