Gamepad APIs: Cross-Platform Button Support

I have implemented the functionality of detecting PlayStation versus Xbox controllers. And, it’s working, to an extent.


Issue 1:
Directional inputs have the same return value. This means, if a player presses a d-pad input, then the code knows it’s a gamepad, but it doesn’t know what type of gamepad. (PlayStation or Xbox? the code is not sure.)


Issue 2:
Can’t have more than one controller plugged in at a time.

Steps:

  1. Play with PS4 remote, move around.
    • The UI updates to show PS4-specific prompts
  2. With PS4 remote still plugged in or connected via Bluetooth, connect an Xbox remote and move around with that.
    • The UI updates to show Xbox-specific prompts
  3. With both remotes connected, switch back to the PS4 remote and move around.
    • You can play the game, but the UI does not update to show PS4-specific prompts. Rather, it thinks the PS4 remote is an Xbox remote as Roblox seems to be relying on the last controller that was connected. (Not the last one to have input.)

This could be an issue with my code, which is below, but I’m not sure.

Anyhow, this means that players, if they want the correct UI prompts to show up, they need to unplug their controller and plug it back in. Or, just only have one remote plugged in at a time.


Code

local userInputService = game:GetService("UserInputService")
local InputTypeChangedRE = game.Workspace.RemoteEventsFolder.Inputs.InputTypeChanged

---

local UserInputTypeSystemModule = {

	gamepadTypeFromNewestInput = "none",
	inputTypeThePlayerIsUsing = "KeyboardAndMouse", --keyboard mouse is default
	gamepadType = "none", -- "none" by default. Can be "Xbox" or "PlayStation"

}

local mouseInputType = {
		Enum.UserInputType.MouseButton1,
		Enum.UserInputType.MouseButton2,
		Enum.UserInputType.MouseButton3,
		Enum.UserInputType.MouseMovement, 
		Enum.UserInputType.MouseWheel	
}

local GamepadInputsList = {
	Enum.KeyCode.ButtonA,
	Enum.KeyCode.ButtonB,
	Enum.KeyCode.ButtonX,
	Enum.KeyCode.ButtonY,

	Enum.KeyCode.ButtonL1,
	Enum.KeyCode.ButtonL2,
	Enum.KeyCode.ButtonL3,

	Enum.KeyCode.ButtonR1,
	Enum.KeyCode.ButtonR2,
	Enum.KeyCode.ButtonR3,

	Enum.KeyCode.ButtonStart,
	Enum.KeyCode.ButtonSelect,
	
	--

	Enum.KeyCode.DPadUp,
	Enum.KeyCode.DPadDown,
	Enum.KeyCode.DPadLeft,
	Enum.KeyCode.DPadRight,
	
	Enum.KeyCode.Thumbstick1,
	Enum.KeyCode.Thumbstick2,


}

local mobileInputType = Enum.UserInputType.Touch

---


local Xbox_ReturnValues_List = {
	
	"ButtonA", -- KeyCode.ButtonA
	"ButtonB", -- KeyCode.ButtonB
	"ButtonX", -- KeyCode.ButtonX
	"ButtonY", -- KeyCode.ButtonY
	"ButtonLB", -- KeyCode.ButtonL1
	"ButtonLT", -- KeyCode.ButtonL2
	"ButtonLS", -- KeyCode.ButtonL3
	"ButtonRB", -- KeyCode.ButtonR1
	"ButtonRT", -- KeyCode.ButtonR2
	"ButtonRS", -- KeyCode.ButtonR3
	"ButtonStart", -- KeyCode.ButtonStart
	"ButtonSelect", -- KeyCode.ButtonSelect 
	
}

local PlayStation_ReturnValues_List = {
	
	"ButtonCross", -- KeyCode.ButtonA
	"ButtonCircle", -- KeyCode.ButtonB
	"ButtonSquare", -- KeyCode.ButtonX
	"ButtonTriangle", -- KeyCode.ButtonY
	"ButtonL1", -- KeyCode.ButtonL1
	"ButtonL2", -- KeyCode.ButtonL2
	"ButtonL3", -- KeyCode.ButtonL3
	"ButtonR1", -- KeyCode.ButtonR1
	"ButtonR2", -- KeyCode.ButtonR2
	"ButtonR3", -- KeyCode.ButtonR3
	"ButtonOptions", -- KeyCode.ButtonStart
	
	"ButtonTouchpad", -- KeyCode.ButtonSelect 
	"ButtonShare", -- KeyCode.ButtonSelect 

}

-- Note: Directional inputs have the same return value. This means, if a player presses a d-pad input, then the code knows it's a gamepad, but it doesn't know what type of gamepad. (PlayStation or Xbox, not sure.) 

---

userInputService.InputBegan:Connect(function(input)
	
	--print(input.KeyCode)
	
	-- Keyboard & Mouse Input: --
	if input.UserInputType == Enum.UserInputType.Keyboard then -- Keyboard inputs --
		if UserInputTypeSystemModule.inputTypeThePlayerIsUsing == "Gamepad" or UserInputTypeSystemModule.inputTypeThePlayerIsUsing == "Touch" then
			print("New InputType detected: Keyboard and Mouse")
			UserInputTypeSystemModule.inputTypeThePlayerIsUsing = "KeyboardAndMouse"
			InputTypeChangedRE:FireServer(UserInputTypeSystemModule.inputTypeThePlayerIsUsing)
			userInputService.MouseIconEnabled = true
			script.Parent:WaitForChild("IngameUI").MobileButtons.Visible = false
			return
		end
	end
	for i, mouseInputs in pairs (mouseInputType) do -- Mouse inputs --
		if input.UserInputType == mouseInputs  then
			if UserInputTypeSystemModule.inputTypeThePlayerIsUsing ~= "KeyboardAndMouse" then
				print("New InputType detected: Keyboard and Mouse")
				UserInputTypeSystemModule.inputTypeThePlayerIsUsing = "KeyboardAndMouse"
				InputTypeChangedRE:FireServer(UserInputTypeSystemModule.inputTypeThePlayerIsUsing)
				userInputService.MouseIconEnabled = true
				script.Parent:WaitForChild("IngameUI").MobileButtons.Visible = false
				return
			end
		end
	end
	----
	
		
	-- Gamepad Input: --

	for i, gamepadInput in pairs (GamepadInputsList) do -- Controller button inputs --
		if input.KeyCode == gamepadInput  then 
									
			if UserInputTypeSystemModule.inputTypeThePlayerIsUsing ~= "Gamepad" then
				print("New InputType detected: Gamepad")
				UserInputTypeSystemModule.inputTypeThePlayerIsUsing = "Gamepad"
				InputTypeChangedRE:FireServer(UserInputTypeSystemModule.inputTypeThePlayerIsUsing)
				userInputService.MouseIconEnabled = false
				script.Parent:WaitForChild("IngameUI").MobileButtons.Visible = false
			end
			
			local stringForKeyCodePressed = userInputService:GetStringForKeyCode(gamepadInput)
			
			for i, retunValue_Xbox in pairs(Xbox_ReturnValues_List) do
				if retunValue_Xbox == stringForKeyCodePressed then
					print(retunValue_Xbox,stringForKeyCodePressed)
					UserInputTypeSystemModule.gamepadTypeFromNewestInput = "Xbox"
				end
			end
			
			for i, returnValue_PlayStation in pairs(PlayStation_ReturnValues_List) do
				if returnValue_PlayStation == stringForKeyCodePressed then
					print(returnValue_PlayStation,stringForKeyCodePressed)
					UserInputTypeSystemModule.gamepadTypeFromNewestInput = "PlayStation"
				end
			end			
			
			if UserInputTypeSystemModule.gamepadTypeFromNewestInput ~= UserInputTypeSystemModule.gamepadType then
				-- player is now using a different type of gamepad than before
				
				UserInputTypeSystemModule.gamepadType = UserInputTypeSystemModule.gamepadTypeFromNewestInput
				InputTypeChangedRE:FireServer(UserInputTypeSystemModule.inputTypeThePlayerIsUsing)

				print(UserInputTypeSystemModule.gamepadType)
			end
					
		end
	end
	----
	
	
	-- Touchscreen input: --
	if input.UserInputType == Enum.UserInputType.Touch then
		if UserInputTypeSystemModule.inputTypeThePlayerIsUsing ~= "Touch" then -- if not mobile/touch input already, make it.
			UserInputTypeSystemModule.gamepadTypeFromNewestInput = "none"
			print("New InputType detected: Touch")
			UserInputTypeSystemModule.inputTypeThePlayerIsUsing = "Touch"
			InputTypeChangedRE:FireServer(UserInputTypeSystemModule.inputTypeThePlayerIsUsing)
			script.Parent:WaitForChild("IngameUI").MobileButtons.Visible = true
			
			local JumpButton_Path = script.Parent.TouchGui.TouchControlFrame:WaitForChild("JumpButton")
			JumpButton_Path.Position = UDim2.fromScale(0.845, 0.715)
			--JumpButton_Path.Size = UDim2.new(JumpButton_Path.Size.X*1.2,JumpButton_Path.Size.Y*1.2)
			
			local ToggleMobileButton_RemoteEvent = game.Workspace.RemoteEventsFolder.UI.ToggleMobileButtons
			ToggleMobileButton_RemoteEvent.OnClientEvent:Connect(function(whatButtonShouldBeToggled,toggleState)
				if whatButtonShouldBeToggled == "JumpButton" then
					if toggleState == false then
						JumpButton_Path.Visible = false
						print("Mobile Jump button toggled OFF")
					elseif toggleState == true then
						JumpButton_Path.Visible = true
						print("Mobile Jump button toggled ON")
					end
				end
			end)
			
			return
		end
	end
	----

end)

return UserInputTypeSystemModule


Here’s a link to my game: PB & Jay | In-dev - Roblox

2 Likes