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.
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.
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 )
game:GetService("ContextActionService"):BindAction("OpenDoorBinding", openDoorFunc, true, "o")
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.
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.
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:
-- 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)
--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.
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.