The Correct Way to Design Mobile Buttons

Introduction

As a game developer, we all love to make our games compatible to all devices, Including mobile!

If you have any mobile buttons in your game, they probably look like this which is good enough…
image_2023-07-29_140832913
:arrow_up: The crouch button is stretched out, isn’t similar to the jump button, and varies on distance from jump button (ex. different screen sizes can make it closer or further away from the jump button.) However, it displays as an icon and not some text making it a bit more easier to see.

image
:arrow_up: This one easy to reach, and doesn’t vary on distance from the jump button, however it uses the legacy mobile button design and uses text instead of an icon. This is fine and it works but the legacy design contradicts the modern Roblox UI.

(no hate to Piggy or FE2 they’re very goated games)

This is what we are going for in this tutorial; a user friendly mobile button relative to the jump button that is easy to use.

Overview:

The way Roblox designs its mobile controls are made to be simple to understand. The jump button doesn’t say ‘jump’, it just has a self-explanatory up arrow. The joystick doesn’t say ‘walk’, that is also self-explanatory. UI doesn’t need to be that complicated, especially for people using a phone… As developers have full control over this, you can even ditch the default controls by disabling GuiService.TouchControlsEnabled and code your own from scratch if you’re looking for more customizable options and such. This tutorial is for making an “addon” to the default ones, as Roblox probably intended for its developers. so that the user has a familiar experience and gets the hang of the controls easily.

According to Roblox Docs, there’s 2 spots for mobile buttons. Here we have the Thumbstick Zone and the Jump Button Zone


Now, Mobile buttons should never be at the Thumbstick Zone! It’s just a very bad idea, as the left thumb should always be used for the thumbstick and it’s very inconvenient to stop moving your character just to press a button. This also can create issues where you try to move your character with the dynamic thumbstick and end up pressing a button instead.

There is something you should be aware of while doing this!
The jump button can change size and position depending on the screen size (ex. phones and tablets)


:arrow_up: Phone

:arrow_up: Tablet
Notice how the jump button’s size and position has changed when the screen size has changed! More specifically, there’s 2 presets of size and position for the controls. The preset changes when any of the screen dimensions reach over 500 pixels.

Getting Started

In StarterGUI, Create a frame. That resembles where the jump button is. A good spot would be {1, -95},{1, -90} with a size of {0, 70},{0, 70}. This is only for editing purposes as it will be changed in a script later on.
Then insert an ImageButton and set it’s position relative to its parent. A good spot for it can be {-1.1, 0},{0, 0} and set its size to {1, 0},{1, 0}* (don’t place it on the right as it won’t be fully visible on the screen.

*Assuming you haven’t modified the anchor point.

Scripting

Insert a script inside the Frame

The often-made mistake that just makes me feel disgusted for some reason is this:
local isMobile = game:GetService('UserInputService').TouchEnabled
This is a very bad idea, since Roblox’s core scripts don’t even use this method. If the user happens to have a touch screen PC, they will see mobile controls without even touching the screen.
You should do this instead:

local UIS = game:GetService("UserInputService") --UserInputService
local Frame = script.Parent

function updateInput()
	local lastInput = UIS:GetLastInputType()
	if lastInput == Enum.UserInputType.Focus then return end -- ignore if the player focuses on the app
	Frame.Visible = lastInput == Enum.UserInputType.Touch -- makes the frame visible if the user touched the screen
end

updateInput() -- Detect input on Startup
UIS.LastInputTypeChanged:Connect(updateInput)

What this does is detect if last UserInputType was Touch, instead of just checking if you can touch the screen. Then it makes the frame visible.

We’re almost done! We just need to make sure that the mobile buttons are in the right place!
For this part, we can take a peek at some of Roblox’s PlayerScripts. More specifically, TouchJump which was written by @jmargh.
image

In this chunk of code, we have exactly what we need to place our jump button.


To explain this code in words, if the absolute length or width of the device safe insets is less than 500, The jump button changes size and position to a value. Otherwise, it changes size and position to another value.

Let’s find a way to replicate this into a LocalScript the easiest way possible!

Adjusting Setup

A way to detect the absolute size of the screen is super simple!
First, Insert a GUI Inside of the frame. It sounds odd, I know. But it will be all worth it
For demonstration purposes, we will be naming this Screen

image
Set Screen’s insets to DeviceSafeInsets
image
Mainly, Screen has the Absolute Size of the device!
image

Scripting (again)

With credit to @jmargh, update the script to this to support the screen sizes.

local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")
local Frame = script.Parent

function updateInput()
	-- Detecting if Buttons should show
	local lastInput = UIS:GetLastInputType()
	if lastInput == Enum.UserInputType.Focus then return end -- ignore if the player focuses on the app
	Frame.Visible = lastInput == Enum.UserInputType.Touch -- makes the frame visible if the user touched the screen
end

updateInput() -- Run this anyway at first.
UIS.LastInputTypeChanged:Connect(updateInput) -- Now you can run only if you need to.

-- Positioning the mobile buttons.
RS.RenderStepped:Connect(function() -- This should always be running
	if Frame.Visible then -- Are mobile buttons enabled?
		local screenSize = Frame.Screen.AbsoluteSize -- Absolute Size of the screen.
		local minAxis = math.min(screenSize.X, screenSize.Y)
		local isSmallScreen = minAxis <= 500 -- Is the screen to small for big mobile buttons?
		local jumpButtonSize = isSmallScreen and 70 or 120 -- Gets the size of the jump button.
		Frame.Size = UDim2.new(0, jumpButtonSize, 0, jumpButtonSize)
		Frame.Position = isSmallScreen and UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize - 20) or UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize * 1.75)
	end
end)

Review

Now you have reached the point where you can now create and place the mobile buttons Easily! If your screen looks like this while emulating a mobile device (using only mouse inputs) You are sooooo close to finishing. Now we need to do the last part: designing the mobile buttons to look like the original.

Designing

Resources required:

After installing paint.net, go in Roblox’s files and open ControlsSheetV2 using paint.net


Save a copy and get started.
So first thing we are going to do is get a blank version of TouchControlsSheetV2. I already made an image of this here so you don’t have to. (don’t open it yet)

Now we need to find an icon for the mobile button. You can find these on Google very easily, or create your own. Then, in a new layer on top of the original one, paste that in and make it a solid color if it isn’t already. Then place it on top of the jump button, with relative size to the arrow.


For demonstration purposes, I didn’t center the image relative to the button. Sometimes, you don’t have to. It’s mostly dependent on the icon you are using. If you are, you’re gonna need to use some math (size of the icon, etc). For reference, in ControlsSheetV2 each button is 144 pixels long and wide

Now use the Magic Wand and the Fill Tool to remove those transparent pixels. Those will cause problems later on.

If you wish to keep your eyes intact and working, I recommend making a background that isn’t white in another layer

Now do what I did here to paste that onto the Jump button

Make a hole where the Jump Button is on the button

Using a little but of math, get the icon on both buttons with the same position relative to their buttons. Here the left of the icon was 44 pixels away from the left of the button (yes the buttons are 1 pixel away from each other, and the left is 1 pixel away from the left side of the image. Use this information, I copied the icon, then used arrow keys/CTRL + arrow keys to move it to perfectly align its left side to the left side of the button, then moved it right 44 pixels

After that, do what you just did coloring the first icon, but with the second one.

There you have it! A modified mobile button sheet for you to use! Now just delete the background layer then merge all layers. Then just cut out parts to export (remember, each button is 144 x 144 pixels

Now just go back to Studio, edit the ImageButton that’s used as a mobile button, then simply upload the images to Image and ImagePressed!

Finished

That was probably a long process. Well, now you’re done! The product should look something like this.
Just like on a keyboard, the input should register when you start pressing the button. Not using the average click functions which require you to hold the button down then up. Use MouseButton1Down, or if you’re that type of guy, use InputBegan.


Also note that the “Active” property can be useful at times, because it doesn’t sink input and allows you to press the button while moving the camera at the same time. This is most used in games where movement really matters like first person shooters, and you probably shouldn’t be using the Activated event if the button isn’t activated.

Resources

:package: Gui Used In This Tutorial|attachment (6.7 KB .rbxm)

Your welcome!
@Micamaster100

131 Likes

This is a great way of supporting mobile devices! Thanks so much for sharing this tutorial!

11 Likes

Just wondering could a similar effect be created using context action service? I believe so, but what are the benefits and cons of each way?

3 Likes

Well I don’t know much about that service, I can assume that my way makes things alot easier to add mobile buttons relative to the jump button. If for any reason, anybody in their right mind would make mobile buttons relative to the Thumbstick, they can just borrow code from the moduleScript I explained about. Simply, I think mine is just alot more better.

3 Likes

You can, just bind an action to a GUI and it should work. You don’t need to bindaction with the default button roblox creates for you.

1 Like

Hey, do you have the place file for this for those who don’t want to follow the tutorial?

4 Likes

The problem with that is it creates legacy buttons

and using ContextActionService:SetImage() just puts an image inside of the old legacy design.

1 Like

There’s a workaround for that. I’ve tried it in the past and it exists within some resources such as shift buttons for mobile.

And what’s that workaround? I’ve been using the TouchJump module in PlayerScripts.PlayerModule.ControlModule and editing that to add more mobile buttons

tysm!! Your steps in the Getting Started and Scripting parts are top-notch!
About the designing part, I didn’t use paint.net. I used GIMP which was much simpler for me to use. (ty again for providing the blank version of TouchControlsSheetV2)

3 Likes

Very in depth tutorial! Thank you for your service :saluting_face:

5 Likes

might use this and make a module…

Edit : I just finished it but it maybe a bit buggy and lacking with features, but its a working module.

2 Likes

finally felt like adding it! check the op :wink: