Over half of all Roblox sessions are played on mobile devices, so it's important to consider cross-platform accessibility when designing an experience for a wide audience. You should aim to support a variety of input devices, including mouse and keyboard inputs and gamepad.
When designing a mobile experience, consider the device orientation that you intend user's to use in your experience, then implement your inputs with ContextActionService to perform the following mobile-related input tasks:
- Create on-screen buttons visible only on mobile devices.
- Customize the mobile buttons and UI to create a unique mobile experience.
- Setup context dependent inputs that allows the same button or input to perform a different action depending on the situation.
- Detect other input devices, such as a mouse or keyboard connected to a mobile tablet, to provide the correct on-screen prompts to the user.
Device Orientation
On phones and tablets, the device orientation majorly affects the user experience and interaction. For example, landscape mode is best operated with two thumbs while portrait mode may lend itself to one-finger interface.
By default, Roblox experiences run in landscape mode, allowing the experience to switch between landscape "left" and landscape "right" as the user's device rotates. However, experiences can be locked to a particular orientation if desired.
Orientation Modes
There are five different orientation modes, including two sensor-based modes and three locked modes.
Sensor Modes | |
---|---|
Landscape Sensor | The default Roblox setting in which the experience always appears in landscape mode (no portrait mode) and the device detects its physical orientation to ensure the experience view is always oriented upward. |
Sensor | The device detects its physical orientation to ensure the experience view is always oriented upward, switching between landscape and portrait mode as needed. |
Locked Modes | |
---|---|
Landscape Left | On devices with a physical home button, the home button is to the left of the display. On devices with a virtual home/nav bar, its touch region is at the bottom of the display. |
Landscape Right | On devices with a physical home button, the home button is to the right of the display. On devices with a virtual home/nav bar, its touch region is at the bottom of the display. |
Portrait | On devices with a physical home button, the home button is below the display. On devices with a virtual home/nav bar, its touch region is at the bottom of the display. |
Orientation Properties
When setting an orientation, you can set the Starting Orientation, the In-Experience Orientation, and the Current Orientation.
Starting Orientation
StarterGui.ScreenOrientation sets the default orientation for a place. Acceptable values include:
Because this property affects all new users who join the experience, you can set its value in StarterGui → Enum.ScreenOrientation within Studio.
In-Experience Orientation
PlayerGui.ScreenOrientation explicitly changes the experience's orientation for a user. When this property is set to one of the Enum.ScreenOrientation enums in a LocalScript, the experience will immediately orient itself to match the setting. This can be useful when an experience needs to provide a particular experience like locking the view to portrait for a minigame.
The following code sample in a LocalScript sets the screen orientation to portrait:
local Players = game:GetService("Players")local playerGUI = Players.LocalPlayer:WaitForChild("PlayerGui")task.wait(2)playerGUI.ScreenOrientation = Enum.ScreenOrientation.Portrait
Current Orientation
PlayerGui.CurrentScreenOrientation gets the current device orientation. Possible values include:
The following code prints the user's current screen orientation:
local Players = game:GetService("Players")local playerGUI = Players.LocalPlayer:WaitForChild("PlayerGui")print(playerGUI.CurrentScreenOrientation)
Character Movement Modes
Roblox offers several StarterPlayer properties you can set to change how users on mobile devices can move through your experience.
You can set mobile movement control schemes for Roblox experiences by changing the values of StarterPlayer.DevTouchMovementMode to one of the following:
Option | Description |
---|---|
ClickToMove | Users can only move through the experience by tapping a target location. This mode includes a jump button in the lower-right region of the screen. Automatic jumping is always active in this movement mode. |
DPad | |
DynamicThumbstick | A dynamic thumbstick appears where the user initially presses down. This mode includes a jump button in the lower-right region of the screen. This is the default user setting for mobile users if UserChoice is set. |
Scriptable | Disables all default controls and allows you to script your own control scheme. |
Thumbpad | |
Thumbstick | A mobile thumbstick located in the lower-left region of the screen. Unlike DynamicThumbstick, the thumbstick position is static and doesn't change position when the user touches on the screen. |
UserChoice | Allows users to choose their desired control scheme from the in-experience Settings menu. This is the default movement mode for experiences. |
Automatic Jumping
When StarterPlayer.AutoJumpEnabled is enabled, the user's character automatically jumps across gaps when approaching the edge of a platform. StarterPlayer.AutoJumpEnabled is enabled by default for mobile devices.
Disable StarterPlayer.AutoJumpEnabled to disable this feature and force users to jump only using their key bindings.
Adding Mobile Buttons
To add mobile buttons, use the ContextActionService:BindAction() method.
The BindAction() method takes the following parameters:
Parameter | Type | Description |
---|---|---|
actionName | string | An identifier string for the action you are binding. You can use the actionName with other functions in ContextActionService to edit the binding. |
functionToBind | function | The function to call when the specified input is triggered. This function receives three arguments:
|
createTouchButton | boolean | When true, creates an on-screen button when the game is running on a mobile device. |
inputTypes | tuple | The inputs you intend to bind to the function, such as enum values from a Enum.KeyCode. |
You can use the following code sample to create an Interact action that creates an on-screen button and also accepts a keyboard and gamepad input:
local ContextActionService = game:GetService("ContextActionService")
local function handleAction(actionName, inputState, inputObject)
if inputState == Enum.UserInputState.Begin then
print(actionName, inputObject)
end
end
-- Bind action to function
ContextActionService:BindAction("Interact", handleAction, true, Enum.KeyCode.T, Enum.KeyCode.ButtonR1)
Removing Mobile Buttons
To remove a mobile button from the screen, call UnbindAction() using the actionName string you passed to BindAction().
Use the following code sample to unbind the previously created Interact action:
-- Unbind action by nameContextActionService:UnbindAction("Interact")
Customizing Button UI
You can use one of the several functions from ContextActionService to customize the on-screen buttons that are created by BindAction().
Button Text
To change the text label for a mobile button, call SetTitle() with the actionName string and a title:
-- Set button label to "Talk"ContextActionService:SetTitle("Interact", "Talk")
Button Image
Mobile buttons can use custom images just like other GUI buttons using the SetImage() method.
Use the following sample code to set a button image, replacing the asset ID with an image of your choice:
-- Set button imageContextActionService:SetImage("Interact", "rbxassetid://0123456789")
Button Position
By default, a new button's position appears near the lower right section of the screen. You should carefully consider button placement on mobile devices and keep in mind the positions of thumbs and hands.
Use the following sample code to set a button's position with the SetPosition() method:
-- Set button positionContextActionService:SetPosition("Interact", UDim2.new(1, -70, 0, 10))
Context-Dependent Inputs
When developing for mobile devices you may often want to change what a single button does based on the context. Since screen space on mobile devices is limited, use contextual buttons that perform different actions based on what the character is able to do.
For example, you can display an active "Collect" button when the user is standing near a chest of gold:
Use the following code sample to create a mobile button that is labelled "Collect" and is bound to the function collectTreasure():
local ContextActionService = game:GetService("ContextActionService")
local function collectTreasure(actionName, inputState, inputObject)
if inputState == Enum.UserInputState.Begin then
print("Collect treasure")
end
end
ContextActionService:BindAction("Interact", collectTreasure, true, Enum.KeyCode.T, Enum.KeyCode.ButtonR1)
ContextActionService:SetPosition("Interact", UDim2.new(1, -70, 0, 10))
-- Set image to blue "Collect" button
ContextActionService:SetImage("Interact", "rbxassetid://0123456789")
At another point in the game, you can change the button to "Talk" when the user is standing near an NPC. Instead of adding and removing the existing button, you can simply use ContextActionService:BindAction() on the existing Interact action, changing the function and button image.
Use the following code sample to set the existing button label to "Talk" and bind it to a function named talkToNPC():
ContextActionService:BindAction("Interact", talkToNPC, true, Enum.KeyCode.T, Enum.KeyCode.ButtonR1)-- Set image to yellow "Talk" buttonContextActionService:SetImage("Interact", "rbxassetid://0011223344")
Detecting Other Devices
In cross-platform experiences, it is necessary to know the user's current device in order to adjust the UI and display correct key binding prompts.
For example, if a user approaches a treasure chest and there's an action bound to collecting the gold, you can show mobile users an on-screen "Collect" button and desktop users an on-screen "T" key icon.
Keep in mind that a mobile device can also have a mouse and keyboard or gamepad plugged in. It is also possible that a desktop has a touchscreen enabled. It is important to reference the user's preferred input options by displaying input options for the actively used device.
In these cases, you can use UserInputService to detect which input devices are enabled. If multiple input devices are enabled, use UserInputService:GetLastInputType() to get the user's last used input device to display on the UI.
You can use the following ModuleScript, placed within ReplicatedStorage and renamed to UserInputModule, to fetch the user's input type, after which you can adapt the UI layout or context to your experience's specific needs.
Use the following ModuleScript to check for enabled input devices and the last used input device:
local UserInputService = game:GetService("UserInputService")
local UserInput = {}
local inputTypeString
-- If device has active keyboard and mouse, assume those inputs
if UserInputService.KeyboardEnabled and UserInputService.MouseEnabled then
inputTypeString = "Keyboard/Mouse"
-- Else if device has touch capability but no keyboard and mouse, assume touch input
elseif UserInputService.TouchEnabled then
inputTypeString = "Touch"
-- Else if device has an active gamepad, assume gamepad input
elseif UserInputService.GamepadEnabled then
inputTypeString = "Gamepad"
end
function UserInput.getInputType()
local lastInputEnum = UserInputService:GetLastInputType()
if lastInputEnum == Enum.UserInputType.Keyboard or string.find(tostring(lastInputEnum.Name), "MouseButton") or lastInputEnum == Enum.UserInputType.MouseWheel then
inputTypeString = "Keyboard/Mouse"
elseif lastInputEnum == Enum.UserInputType.Touch then
inputTypeString = "Touch"
elseif string.find(tostring(lastInputEnum.Name), "Gamepad") then
inputTypeString = "Gamepad"
end
return inputTypeString, lastInputEnum
end
return UserInput
Once the UserInputModule script is in place, use the following code sample in a LocalScript to get the user's last input type:
local ReplicatedStorage = game:GetService("ReplicatedStorage")-- Require modulelocal UserInputModule = require(ReplicatedStorage:WaitForChild("UserInputModule"))local currentUserInput, inputEnum = UserInputModule.getInputType()print(currentUserInput, inputEnum)