Accessing controls across different platforms

I’ve been unable to find anything on how controls on different platforms translate to PC equivalents (I know at least that tapping on a phone/tablet is an M1 click), or on how to access input such as tablet movement controls.
Since most of my players are non-PC, I really should be catering to them.

1 Like

Look into UserInputService, they have platform-specific controls.

Doesn’t help.
It says everything but what I asked.

What information are you looking for specifically?

If you are designing cross-platform input controls for your game, UserInputService (access all types of input devices and their data) and ContextActionService (bind actions to input events across all platforms) are the way to go. By building these into your game from the start, you will be able to link keyboard keys, touchscreen buttons and gamepad buttons to the same input actions.

If you are just looking for information on how different inputs translate to actions Roblox defaults by default, the most basic behavior (click) can be activated through:

  • Mouse click
  • Touch tap
  • Gamepad right trigger press (clicks at cursor position)

If there is any specific inputs you are looking for, feel free to ask.

4 Likes

I don’t think you have access to the default mobile/tablet control buttons if that’s what you’re asking. You’ll need to make your own custom controls.

Movement input.
I have a fly script that converts WASD and space to a unit vector for flying direction relative to camera, and I need to be able to produce a vector like that on different platforms.

Convert Humanoid.MoveDirection to a vector relative to the camera? It might not work but anything’s possible

1 Like

It says that it’s for the Move() method only, but I tested it and it works for regular movement too.
Now I need to find out if I can figure out if jump is held continuously on non-PC clients.

IIRC, you should be able to check if Humanoid:GetState == Enum.Humanoid.Jumping

Using Humanoid.MoveDirection is the best way to tap into the movement scripts, so if you make your fly script read from that value it will automatically work across all devices :slight_smile:

Alright, I think I’ve figured out a way to figure out if the jump key is held on any platform.
JumpRequest fires every time the jump button is pushed, no matter what that button is.
That will also fire an InputBegan event at the exact same time, telling me what to check for in the InputEnded event to know when the jump button is no longer held.

As for figuring out which InputBegan event relates to the jump button push, it’s simply the one that’s closest.
The closest is found by storing the time of the last InputBegan event, and when the jump request fires then the first InputBegan event to fire is assumed to be the correct one since it’s the closet in time. At least, until a span of time passes such that an new InputBegan event would be further after the jump request than the last InputBegan was before the jump request.

1 Like

Alright, here it is, a function that when invoked will cause the variable “jumping” to say whether the jump button is held, no matter what device is used.
It’d be better if I could give the variable to write to as an argument, but this is Lua so as far as I know I can’t give a pointer to a boolean instead of a boolean value.

One odd quirk in the function is return (input.Position-jumpInput.Position).magnitude == 0, which obviously doesn’t make sense since checking for zero magnitude is the same as checking for equality, and the Vector3 class compares values instead of pointers. However, I’ve found that comparing input.Position with jumpInput.Position always returns false, even if the magnitude of their difference is zero; I’m suspecting that Vector3 has a doppelganger that only compares pointers.

jumping = false
function identifyWhenJumping()
	local closestInput
	local closestInputTime
	local waitForChange
	
	local checkJumpEnded
	
	InputService.InputBegan:Connect(
		function(input)
			closestInput = input
			closestInputTime = tick()
			waitForChange = false
	end)
	
	local jumpRequestBusy = false
	InputService.JumpRequest:Connect(
		function()
			if jumping == false and jumpRequestBusy == false then
				jumpRequestBusy = true
				waitForChange = true
				local newInput
				if closestInputTime then
					newInput = waitUntilTimeout(InputService.InputBegan, tick()-closestInputTime) --An overwrite of closestInput can only be closer in this time window
				else
					newInput = InputService.InputBegan:Wait() --In this case, the first ever input occurs after JumpRequest.
				end
				if newInput then
					while waitForChange do wait() end
				end
				
				local jumpInput = closestInput
				
				if jumpInput.KeyCode == Enum.KeyCode.Unknown then
					checkJumpEnded = function(input)
						return (input.Position - jumpInput.Position).magnitude == 0 --Jump activated by screen button, release will always be reported in same position
					end
				else
					checkJumpEnded = function(input)
						return input.KeyCode == jumpInput.KeyCode --Jump activated by physical button, simply checks if button released is the button
					end
				end
				
				jumping = true
				jumpRequestBusy = false
			end
		end)
	
	InputService.InputEnded:Connect(
		function(input)
			if jumping then
				if checkJumpEnded(input) then
					jumping = false
				end
			end
	end)
end

function waitUntilTimeout(event, timeout)
    local Signal = Instance.new('BindableEvent')
    local conn = nil
    conn = event:Connect(function(...)
        conn:Disconnect()
        Signal:Fire(...)
    end)

    delay(timeout, function()
        if (conn ~= nil) then
            conn:Disconnect()
            conn = nil
            Signal:Fire(nil)
        end
    end)

    return Signal.Event:Wait()
end
1 Like
	if closestInputTime then
		newInput = InputService.InputBegan:Wait(tick()-closestInputTime) --An overwrite of closestInput can only be closer in this time window
	else
		newInput = InputService.InputBegan:Wait() --In this case, the first ever input occurs after JumpRequest.
	end

FYI RBXScriptSignal’s :Wait() does not take any parameters, so you’re doing the same thing in both branches of this if-statement.

1 Like

In that case my code is working purely because the input event always follows the jump request.
Which seems odd, since the control input triggers the jump request.

I designed the code assuming that there was a chance their order would be reversed, and I’ll still build that extra security into it since a single order reversal (which could occur for a number of reasons) would break the script permanently.
I’ve improved it by including Merely’s waitUntilTimeout function, changed to use BindableEvents since his direct access of Roblox signals isn’t even possible anymore as far as I know.

1 Like