I have a very simple local script in starterplayerscripts that disables and then enables the player’s controls. It seems that it doesn’t work anymore, as it throws an error directing to a completely different script. See below.
My code:
wait(2)
local LocalPlayer = game:GetService("Players").LocalPlayer
local Controls = require(LocalPlayer.PlayerScripts.PlayerModule):GetControls()
Controls:Disable()
wait(2)
Controls:Enable()
After it gets to Controls:Disable(), it throws this error:
Players.xasc.PlayerScripts.PlayerModule.ControlModule:274: attempt to index nil with 'Enable'
Stack begin
Script 'Players.xasc.PlayerScripts.PlayerModule.ControlModule', Line 274
Script 'Players.xasc.PlayerScripts.PlayerModule.ControlModule', Line 326 - function UpdateActiveControlModuleEnabled
Script 'Players.xasc.PlayerScripts.PlayerModule.ControlModule', Line 360 - function Disable
Script 'Players.xasc.PlayerScripts.LocalScript', Line 5
Stack end
I have never experienced this before. What should I do?
This is the reason why I do not revolve my code around something that is subject to change at any moment. If you would like to use my private Controls module, feel free to grab it below.
It’s new so the API is pretty small, but I’ll expand on it later on.
API & Examples
-- Disables the controls by sinking the inputs via ContextActionService for Keyboard/Controller
-- and moves the TouchGui out of the screen to prevent touch inputs (simply changing the
-- Visible property wont work).
Controls:Disable()
-- Enables the controls.
Controls:Enable()
-- Checks if the controls are enabled.
local isEnabled: boolean = Controls:IsEnabled()
-- You can also grab the TouchGui if it exists.
local touchGui: ScreenGui? = Controls:GetTouchGui()
-- You can also listen for these events anytime
Controls.Toggled:Connect(function(enabled: boolean): ()
-- code
end)
Controls.TouchGuiAdded:Connect(function(touchGui: ScreenGui): ()
-- code
end)
Controls.TouchGuiRemoved:Connect(function(touchGui: ScreenGui): ()
-- code
end)
-- You can also check if the input is a gamepad. It returns the connected gamepad # as well.
-- It will return false, 0 if it is not a gamepad.
local isGamepad: boolean, connectedGamepad: number = Controls:IsInputObjectGamepad(inputObject: InputObject)
local isGamepad: boolean, connectedGamepad: number = Controls:IsUserInputTypeGamepad(inputType: Enum.UserInputType)
-- This also fixes the multiple firing issue with UserInputService.JumpRequest.
-- It works for keyboard, gamepad, and touch screen.
Controls.JumpRequest:Connect(function(): ()
-- code
end)
Controls.lua
--!strict
-- Controls.lua
-- UIScript
-- 21 April 2024
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local GuiService = game:GetService("GuiService")
local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer.PlayerGui
local Signal = require(script.Parent.Signal)
local Toggled: Signal.Signal<boolean> = Signal.new()
local TouchGuiAdded: Signal.Signal<ScreenGui> = Signal.new()
local TouchGuiRemoved: Signal.Signal<ScreenGui> = Signal.new()
local JumpRequest: Signal.Signal<> = Signal.new()
local Controls = {
Toggled = Toggled,
TouchGuiAdded = TouchGuiAdded,
TouchGuiRemoved = TouchGuiRemoved,
JumpRequest = JumpRequest
}
local touchGuiConnections = {
Toggled = nil :: Signal.Connection?,
}
function Controls:IsUserInputTypeGamepad(inputType: Enum.UserInputType): (boolean, number)
for connectedGamepad: number = 1, 8 do
if inputType == Enum.UserInputType[`Gamepad{connectedGamepad}`] then
return true, connectedGamepad
end
end
return false, 0
end
function Controls:IsInputObjectGamepad(inputObject: InputObject): (boolean, number)
return Controls:IsUserInputTypeGamepad(inputObject.UserInputType)
end
function Controls:GetTouchGui(): ScreenGui?
local touchGui: ScreenGui? = PlayerGui:FindFirstChild("TouchGui") :: ScreenGui
local touchControlFrame: Frame? = if touchGui ~= nil then touchGui:FindFirstChild("TouchControlFrame") :: Frame else nil
if touchControlFrame ~= nil and touchControlFrame.ClassName == "Frame" then
return touchGui
end
return nil
end
function Controls:IsEnabled(): boolean
return ContextActionService:GetAllBoundActionInfo()["Controls_Disable"] == nil
end
function Controls:Enable(): ()
if Controls:IsEnabled() == false then
ContextActionService:UnbindAction("Controls_Disable")
Toggled:Fire(true)
end
end
function Controls:Disable(): ()
if Controls:IsEnabled() == true then
local function handleControlsAction(_: string, inputState: Enum.UserInputState, inputObject: InputObject): Enum.ContextActionResult
if inputObject.UserInputType == Enum.UserInputType.Keyboard then
return Enum.ContextActionResult.Sink
elseif Controls:IsInputObjectGamepad(inputObject) == true and ((inputObject.KeyCode == Enum.KeyCode.Thumbstick1 and inputState == Enum.UserInputState.Change) or inputObject.KeyCode == Enum.KeyCode.ButtonA) then
return Enum.ContextActionResult.Sink
end
return Enum.ContextActionResult.Pass
end
ContextActionService:BindActionAtPriority(
"Controls_Disable",
handleControlsAction,
false,
Enum.ContextActionPriority.High.Value + 1,
Enum.PlayerActions.CharacterForward,
Enum.PlayerActions.CharacterBackward,
Enum.PlayerActions.CharacterLeft,
Enum.PlayerActions.CharacterRight,
Enum.PlayerActions.CharacterJump,
Enum.UserInputType.Gamepad1
--Enum.UserInputType.Gamepad2,
--Enum.UserInputType.Gamepad3,
--Enum.UserInputType.Gamepad4,
--Enum.UserInputType.Gamepad5,
--Enum.UserInputType.Gamepad6,
--Enum.UserInputType.Gamepad7,
--Enum.UserInputType.Gamepad8
)
Toggled:Fire(false)
end
end
do
local function onControlsToggled(enabled: boolean): ()
local touchGui: ScreenGui? = Controls:GetTouchGui()
local touchControlFrame: Frame? = if touchGui ~= nil then touchGui:FindFirstChild("TouchControlFrame") :: Frame else nil
if touchGui ~= nil and touchControlFrame ~= nil then
touchControlFrame.Visible = enabled
touchControlFrame.Position = UDim2.fromScale(0, if enabled == true then 0 else 1)
end
end
onControlsToggled(Controls:IsEnabled())
local function onPlayerGuiChildAdded(child: Instance): ()
if child.ClassName == "ScreenGui" and child.Name == "TouchGui" then
if touchGuiConnections.Toggled == nil then
touchGuiConnections.Toggled = Toggled:Connect(onControlsToggled)
end
Controls.TouchGuiAdded:Fire(child :: ScreenGui)
local touchControlFrame: Frame? = child:FindFirstChild("TouchControlFrame") :: Frame
local jumpButton: GuiButton? = if touchControlFrame ~= nil then touchControlFrame:FindFirstChild("JumpButton") :: GuiButton else nil
if jumpButton ~= nil and jumpButton:IsA("GuiButton") == true then
local function onJumpButtonInputBegan(): ()
JumpRequest:Fire()
end
jumpButton.InputBegan:Connect(onJumpButtonInputBegan)
end
end
end
local function onPlayerGuiChildRemoved(child: Instance): ()
if child.ClassName == "ScreenGui" and child.Name == "TouchGui" then
local touchControlFrame: Frame? = child:FindFirstChild("TouchControlFrame") :: Frame
if touchControlFrame ~= nil and touchControlFrame.ClassName == "Frame" then
if touchGuiConnections.Toggled ~= nil then
touchGuiConnections.Toggled:Disconnect()
touchGuiConnections.Toggled = nil
end
TouchGuiRemoved:Fire(child :: ScreenGui)
end
end
end
PlayerGui.ChildAdded:Connect(onPlayerGuiChildAdded)
PlayerGui.ChildRemoved:Connect(onPlayerGuiChildRemoved)
for _: number, child: Instance in PlayerGui:GetChildren() do
task.spawn(onPlayerGuiChildAdded, child)
end
local function handleJumpAction(_: string, inputState: Enum.UserInputState): Enum.ContextActionResult
if inputState == Enum.UserInputState.Begin then
JumpRequest:Fire()
end
return Enum.ContextActionResult.Pass
end
ContextActionService:BindActionAtPriority(
"Controls_JumpAction",
handleJumpAction,
false,
Enum.ContextActionPriority.Medium.Value + 1,
Enum.PlayerActions.CharacterJump,
Enum.KeyCode.ButtonA
)
end
return Controls
You will also need @sleitnick’s Signal you can grab here: