ContextActionService tutorial

Porting games from a PC environment to a mobile enviroment can be difficult, especially when it comes to input. A computer has a wealth of input from the keyboard to the mouse, whereas a mobile device, such as an iPad only has touch. One solution is to use the ContextActionService. This service allows you to bind a function to traditional pc input and at the same time creates an on screen button only visible on a mobile device.

Example of a touch button in game

Additionally, mobile screen space is at a premium, and sometimes it is desirable for on screen buttons to only show when relevant. For example, let's say your game has doors and there is a special player action to open a door. It doesn't make sense to show an "Open Door" button all game long. Instead, the button should only show when the player is near a door, and should disappear when the player has moved a distance away. Even in a PC game, sometimes it makes sense for a certain key to perform an action under specific circumstances and not in others. ContextActionService allows for very quick and easy manipulation of when input is tied to functions, as well as when mobile action buttons are shown or not.

Usage[edit]

To use the ContextActionService first a reference has to be declared in a Local Script. This service will not work in a Server Script as the server does not handle input from players.

local contextAction = Game:GetService("ContextActionService")

Bind function (create mobile button)[edit]

After the ContextActionService has been declared, the BindAction function can be used. This function will associate specified input with the function that should be called when the input is entered. This function will also display a mobile button if desired. This function takes several parameters:

void ContextActionService:BindAction(
    string actionName,
    function functionToBind,
    bool createTouchButton,
    Tuple inputTypes
)
actionName
This string is a key used by other functions in ContextActionService to manipulate the binding.
functionToBind
This is the function you want to be called when the specified input is triggered. This function can be defined earlier in the script or can be defined inline. This function will be called with three arguments: a string that is the actionName that is calling this function, a Enum.UserInputState that defines the state the input was in when it called the function, and lastly the InputObject that caused the call to the function.
createTouchButton
This boolean indicates whether or not you want a button to be created when your game is running on a mobile device. If you are using this service just to bind input for PC controls, then you can set this to false. Otherwise this should be true if you want a button when your game is played on a mobile device.
inputType
This tuple contains all of the input you want to bind to the function. This tuple can contain values from Enum.KeyCode, Enum.UserInputType, or simple strings corresponding to keys on a keyboard.
game:GetService("ContextActionService"):BindAction("OpenDoorBinding", openDoorFunc, true, "o")

Unbind function (remove mobile button)[edit]

When you no longer want input tied to a function, or if you want to remove an mobile button from the screen, use UnbindAction. This function takes a string as an argument which is the key defined in BindAction. You can also use UnbindAllActions to remove all functions that have been bound.

game:GetService("ContextActionService"):UnbindAction("OpenDoorBinding")

Modifying Button[edit]

ContextActionService provides several functions to manipulate the buttons that are generated by binding functions. A button can use a custom image, much like an ImageLabel by using SetImage and providing the name of the binding and the url of the image.

The button can also be positioned using SetPosition and providing the name of the binding and the UDim2 where you want the button to be positioned. If you do not specify this the button will appear near the jump button on the right hand side of the screen.

Example[edit]

In this game there is a teleport pad that can take a player to the destination when they step on it. In this case, we won't actually teleport until the user presses a button. We could make a new GUI element for this button, but we only want the button to appear when the user is on the teleporter. Using ContextActionService, we can bind a teleport function to a button when the player is in the right spot which will show the button, and then unbind it when the player leaves which will remove the button. Note this button will only show up on mobile devices. To test on a PC or Mac we will also bind the "t" key on the keyboard to the action:

Select
-- Make variables for the local player and ContextActionService
local player = game.Players.LocalPlayer
local ContextActionService = game:GetService("ContextActionService")
 
-- Define teleport function which we will bind to a button
function teleportPlayer(actionName, actionInputState, actionInputObject)
	player.Character.Torso.CFrame = game.Workspace.DestinationPad.CFrame + Vector3.new(0, 4, 0)
	-- Remove binding to hide button after teleport
	ContextActionService:UnbindAction("teleport")
end
 
-- Setup Touched and TouchEnded events. Also making a boolean to keep track of whether we have bound the function or not.
local playerOnPad = false
game.Workspace.TeleportBeam.Touched:connect(function(part)
	if part and part.Parent == player.Character then
		if not playerOnPad then
			ContextActionService:BindAction("teleport", teleportPlayer, true, "t")
		end
		playerOnPad = true
	end
end)
 
game.Workspace.TeleportBeam.TouchEnded:connect(function(part)
	if part and part.Parent == player.Character then
		if playerOnPad then
			ContextActionService:UnbindAction("teleport")
		end
		playerOnPad = false
	end
end)

Working with Gamepads[edit]

ContextActionService also provides an easy way to support Gamepad context actions in your game. It works the same way as any input, so something like this:
Select
--actionName will be a string that corresponds to the name the action was given (in this case "openDoor")
--actionInputState will be the Enum.UserInputState that the input was in when this function was called
--actionInputObject will be the inputObject that called this function
local openDoorFunction = function(actionName, actionInputState, actionInputObject)
     if actionInputState == Enum.UserInputState.Begin and actionInputObject.UserInputType == Enum.UserInputType.Gamepad1 then
          doOpenDoorFunction()
     end
end
 
game:GetService("ContextActionService"):BindAction("openDoor", openDoorFunction, false, Enum.KeyCode.ButtonX)


Will make any time ButtonX is pressed or released call the function openDoorFunction. It is important to note that this will be called for all gamepads, so you should check and make sure the gamepad calling it is the one you want.

Overriding Inputs[edit]

Suppose I have code like this:
Select
game:GetService("ContextActionService"):BindAction("openDoor", openDoorFunction, false, Enum.KeyCode.ButtonX)
game:GetService("ContextActionService"):BindAction("changeWeapon", changeWeaponFunction, false, Enum.KeyCode.ButtonX)


What happens when I press ButtonX? The changeWeaponFunction will be called, and the openDoorFunction will NOT be called. This is because changeWeaponFunction was bound last, so it takes precedence. If the "changeWeapon" action was unbound later, then the "openDoor" action will be called for ButtonX in the future.

What happens the moment I bind an input that is already bound? The currently bound function is called, but with a special UserInputState called Enum.UserInputState.Cancel. This is to allow the currently bound function to do any clean up it might want to do (Ex: you have Enum.KeyCode.Thumbstick1 bound to controlling the character, but you bind a menu action to thumbstick1 over that action. The character movement function will receive a cancel event so it can stop the character if it is currently moving.) If the latter bound function unbinds itself later, the original bound function will be called with Enum.UserInputState.End indicating that the original bound function can resume handling input.