REVISITING: ContextActionService:GetButton() Button Limit?

I’d like to revisit the issue raised in these two posts:

  1. ContextActionService:GetButton() Button Limit?
  2. ContextActionService:GetButton() Doesn’t Work With More Than 7 Buttons

Last year @PhoenixSigns made this post outlining a bug in ContextActionService:Get(Button). After much discussion @Ozzypig suggested to set the mobile button generation to false when BindAction is called and manage buttons elsewhere.

Ozzypig:

My suggestion is never rely on the touch buttons created by BindAction - aka always keep the 3rd argument to ContextActionService false. Create your own buttons using TextButton/ImageButton, and call your action handler manually according to InputBegan/InputChanged/InputEnded events firing. It’s a bit of a non-solution to your question, but I’ve always considered CAS buttons an OK temporary solution until you implement your own touch UI.

Is that really the solution? This seems horribly inadequate. Not only can you not call :GetButton, but the use of :SetTitle also causes the error. One challenge is that every button is called “ContextActionButton” in …LocalPlayer.PlayerGui.ContextActionGui.ContextButtonFrame. If these buttons were named by their actionName then they could be manipulated easily.

If this is an intended limit, please tell me how I can use context bindings with custom buttons.

Thanks and #firstpost !

What’s your support question here?

If you’re requesting for a documentation update, please use #platform-feedback:developer-hub. If you are wanting to report a bug regarding ContextActionService buttons, #platform-feedback:engine-bugs. If you’re requesting a change in the limit, #platform-feedback:engine-features.

That, or are you asking how you can use context bindings with custom buttons to avoid the limit?

Thanks, I have modified the post to ask more plainly using your suggested language:
How I can use context bindings with custom buttons?

I’ll clarify what I meant. Basically, the buttons created from ContextActionService are pretty limited. You don’t want to be using them as your be-all end-all solution to on-screen, touch UI. At the end of the day, you should be making your own UI in your own ScreenGui, with some number of TextButtons or ImageButtons within it.

Remember when you bind an action to some inputs, you’re sending the action handler function. It usually looks like this:

local function handleAction(actionName, inputState, inputObject)
    if actionName == "DoTheThing" then
        if inputState == Enum.UserInputState.Begin then
            doTheThing()
        end
    end
end

Remember, you can always call this function yourself manually. So, say you have GuiButton which you’ve expertly customized to fit your game (and perhaps you even have more than 7). You don’t need to go through CAS to do it. Just do something like this:

local guiButton = script.Parent
guiButton.InputBegan:Connect(function (inputObject)
    handleAction("DoTheThing", Enum.UserInputState.Began, inputObject)
end)
guiButton.InputChanged:Connect(function (inputObject)
    handleAction("DoTheThing", Enum.UserInputState.Changed, inputObject)
end)
guiButton.InputEnded:Connect(function (inputObject)
    handleAction("DoTheThing", Enum.UserInputState.Ended, inputObject)
end)

Alternatively, you could just call doTheThing(), which is an abstract representation of whatever the action “DoTheThing” is supposed to do. Whichever you do will depends on if you need the UserInputState or InputObject itself (necessary for some touch things). I’ve built systems where this has been true, but again, depends on your case.

Perhaps your case is more complicated, like putting the screen-space equivalent of some keyboard key, and you want to do whatever action is relevant for that keyboard key through CAS. In that case, there’s probably a better way to do this than through CAS. Whatever your case might be, you should be transparent about what the context and actions are, so someone can point you in the right direction.

Edit: Here’s a $#!tty MS Paint diagram from 2 AM that might explain it better.

3 Likes

I am building a multi-function painting tool and there are many contexts and actions which is why I didn’t go into detail on the context and actions. One example is invoking custom GUIs based on TouchEnabled or GamepadEnabled using the inputState. Pretty straight forward. Another example bypasses an inputState check and uses RenderStepped to temporarily take over the camera. I’ve also used BindActionAtPriority to temporarily override inputs such as right-click.

Thank you for putting this response and diagram together. You have demystified CAS for me. I was thinking it did more than it does but the reality is that it is a great binding system that has a simple button generation feature for convenience or prototyping. I was scripting customizations to those buttons instead of using my own :man_facepalming:t2: That was part of the reason I was frustrated that they were all named the same thing! When I started bypassing the ActionHandler to doTheThing() it felt like a work around and I was getting off track. I appreciate the thorough explanation and for reminding me of GuiObject.Input* as I had overlooked this.

1 Like