How do you fully disable all character controls?

I was under the impression that starting a LocalScript in the StarterPlayerScripts folder with the following code would disable all character controls:

local oldController = require(script.Parent:WaitForChild("PlayerModule")):GetControls()
oldController:Disable()

However, this seems to only disable keyboard controls. When I test this in play solo and move around the joystick of my gamepad, my character will still be able to move. On top of that, using a gamepad and then switching back to keyboard seems to automatically re-enable keyboard controls.

Is this method of disabling controls incorrect, or is there something else going on here?

1 Like

When I want to toggle character controls I typically do the following:

  • Set WalkSpeed and JumpPower to 0
  • Anchor HumanoidRootPart (might not be suitable depending on your game)
  • Set UserInputService.ModalEnabled which sets the visibility of on-screen virtual gamepad controls for mobile

My script for this looks something like the following:

local function setCharacterInputsEnabled(enabled: boolean)
	-- UserInputService.ModalEnabled was fixed as of Roblox version 481
	-- https://developer.roblox.com/en-us/resources/release-note/Release-Notes-for-481
	UserInputService.ModalEnabled = not enabled

	-- This could yield if the character is still spawning
	local character = localPlayer.Character or (localPlayer.CharacterAdded:Wait())
	if character then
		-- Set WalkSpeed and JumpPower if humanoid is present
		local humanoid = character:WaitForChild("Humanoid")
		if humanoid and humanoid:IsA("Humanoid") then
			-- TODO: Define default speed/jump values somewhere in case these change
			humanoid.WalkSpeed = enabled and 16 or 0
			humanoid.JumpPower = enabled and 50 or 0
		end

		-- Anchor character if there is a root part
		local rootPart = character:WaitForChild("HumanoidRootPart")
		if rootPart and rootPart:IsA("BasePart") then
			rootPart.Anchored = not enabled
		end
	end
end

This approach works perfectly for me, but there might be a better way of approaching the issue.

2 Likes

You can as well by using ContextActionService bind an action to unpack(Enum.PlayerActions:GetEnums()). Enum.PlayerActions should be generic working for any sort of input as long as it does the job of moving the player.

Returning anything but Enum.ContextActionResult.Pass will be considered the same as returning Enum.ContextActionResult.Sink. If Enum.ContextActionResult.Sink is returned from an action with a higher priority all actions of lower priority bound to the same key will be ignored. That way, if the action you bound is of higher priority than the scripts handling player movement (using BindActionAtPriority) returns anything but Enum.ContextActionResult.Pass along with doing no other side effects, the player should not move.

I am unsure of the priority order, but the wiki states that higher is more prioritized, so I guess you can put a math.huge.

2 Likes

I don’t have a solution but I looked into ControlModule and this seems to be the case. The Disable method only disables the active controller – it doesn’t stop you from selecting a new one.

function ControlModule:Disable()
	if self.activeController then
		self.activeController:Enable(false)

		if self.moveFunction then
			self.moveFunction(Players.LocalPlayer, Vector3.new(0,0,0), true)
		end
	end
end

To do that, you would need to prevent any calls to ControlModule:SwitchToController() or have an isEnabled check in the method itself.

2 Likes