0
\$\begingroup\$

I have two buttons on my canvas: Play and Option. I use arrows to highlight the selected button. If I select any button and press space from the keyboard, it should active the button without needing to click on it with mouse.

I have already tried doing so with if(button_option== EventSystem.current.currentSelectedGameObject) { Debug.Log(" option selected " ); }, but it simply did not work.

How do I activate buttons using the keyboard?

\$\endgroup\$
3
  • \$\begingroup\$ I tried this. but not work good: if(button_option== EventSystem.current.currentSelectedGameObject) { Debug.Log(" option selected " ); } \$\endgroup\$
    – user43474
    Sep 3 '16 at 3:29
  • \$\begingroup\$ I can't work out if you still intend to offer the ability to activate the buttons with the mouse. If you don't, you should remove the Button component: you only need the Image to "react via keyboard". If you do use mouse input, simply ensure you point to the same method you call from your onClick listener to match mouse interaction with the provided keyboard interaction. \$\endgroup\$
    – Gnemlock
    Sep 4 '16 at 3:46
  • 1
    \$\begingroup\$ Unity supports keyboard interaction with UI buttons out of the box. The only thing I've found I need to do is add a script to select one button when the scene/menu loads, using button.Select(), so that the keyboard navigation has somewhere to start from. \$\endgroup\$
    – DMGregory
    Sep 4 '16 at 12:18
3
\$\begingroup\$

In theory, you shouldn't use a keyboard to activate your buttons. You should use the keyboard to activate the same methods as your buttons. You use your buttons to provide direct interface with the actions you want those buttons to represent - why take the extra time to trigger the event through the button, when you can call it directly from your code?

As for selecting your buttons, you can handle that as its own event and use code to trigger the "on" or "off" states, as well as determine the correct method to invoke when you hit space. I have provided two solutions to achieve this goal; both may involve concepts you are unfamiliar with. If that is the case, follow the links and read up on them. Otherwise, it will be could be much harder to extend the implementation to include more buttons, in the future.

In such form, if you do not intend to offer mouse interaction of the buttons, you should probably remove the Button component. The below implementations rely entirely on the Image component. If you do wish to provide mouse interaction, ensure that the onClick() methods you invoke match the methods you invoke for keyboard input. The below solutions will not match the onClick() implementations on the same object, unless you ensure they call the same method.

Both implementations give the following results:

An animation of the results, play testing in scene view. We can see that the buttons select correctly with the arrow keys, and correctly call the correct function.


Using a manual struct to house your "button" data

In the below implementation, we use the struct and delegate function to create an array of objects that individually reference an Image and the desired method we intend to call when the user presses space.

using UnityEngine;
using UnityEngine.UI;

public class ButtonHandler : MonoBehaviour
{
    ///<summary>Placeholder delegate function for our buttonList</summary>
    public delegate void ButtonAction();
    ///<summary>Array of buttons, created from a struct, below.</summary>
    public MyButton[] buttonList;
    ///<summary>Index reference to our currently selected button.</summary>
    public int selectedButton = 0;

    void Start()
    {
        // Instantiate buttonList to hold the amount of buttons we are using.
        buttonList = new MyButton[2];
        // Set up the first button, finding the game object based off its name. We also 
        // must set the expected onClick method, and should trigger the selected colour.
        buttonList[0].image = GameObject.Find("PlayButton").GetComponent<Image>();
        buttonList[0].image.color = Color.yellow;
        buttonList[0].action = PlayButtonAction;
        // Do the same for the second button. We are also ensuring the image colour is
        // set to our normalColor, to ensure uniformity.
        buttonList[1].image = GameObject.Find("OptionsButton").GetComponent<Image>();
        buttonList[1].image.color = Color.white;
        buttonList[1].action = OptionsButtonAction;
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.LeftArrow))
        {
            MoveToNextButton();
        }
        else if(Input.GetKeyDown(KeyCode.RightArrow))
        {
            MoveToPreviousButton();
        }

        if(Input.GetKeyDown(KeyCode.Space))
        {
            buttonList[selectedButton].action();
        }
    }

    void MoveToNextButton()
    {
        // Reset the currently selected button to the default colour.
        buttonList[selectedButton].image.color = Color.white;
        // Increment our selected button index by 1.
        selectedButton++;
        // Check that our new index does not move outside of our array.
        if(selectedButton >= buttonList.Length)
        {
            // If you want to reset to the first button, reset the index.
            selectedButton = 0;
            // If you do not, simply move it back by 1, instead.
        }
        // Set the currently selected button to the "selected" colour.
        buttonList[selectedButton].image.color = Color.yellow;
    }

    void MoveToPreviousButton()
    {
        // Should be self explanatory; similar in function to MoveToNextButton,
        // but instead, we are moving back a button.
        buttonList[selectedButton].image.color = Color.white;
        selectedButton--;
        if(selectedButton < 0)
        {
            selectedButton = (buttonList.Length - 1);
        }
        buttonList[selectedButton].image.color = Color.yellow;
    }

    ///<summary>This is the method that will call when selecting "Play".</summary>
    void PlayButtonAction()
    {
        Debug.Log("Play");
    }

    ///<summary>This is the method that will call when selecting "Options".</summary>
    void OptionsButtonAction()
    {
        Debug.Log("Options");
    }

    ///<summary>A struct to represent individual buttons. This makes it easier to wrap
    /// the required variables into a single container. Don't forget 
    /// [System.Serializable], if you wish to see your final array in the inspector.
    [System.Serializable]
    public struct MyButton
    {
        /// <summary>The image contained in the button.</summary>
        public Image image;
        /// <summary>The delegate method to invoke on action.</summary>
        public ButtonAction action;
    }
}

Using an automatically generated array to broadcast method calls

A simpler, less controlled way to reach similar functionality is with the simple use of this.BroadcastMessage(). This option gives greater flexibility, as you do not need to manually provide the details of each button, nor the specific location of each "button action".

This comes with two specific requirements1. First and foremost, the script must be on a game object that has your Canvas component as its child. Secondly, all "button actions" must also be accessible within the object holding your script and its children. They can attached as individual scripts to the actual buttons, they can be in a seperate "button actions" script attached to the Canvas object, or they can even be in the same script (as seen in the example). It doesn't really matter.

For the below example, we use the name of the "button" object. A button with the object name of "Play" will invoke PlayAction(). A button with the object name of "Options" will invoke OptionsAction().

using UnityEngine;
using UnityEngine.UI;

public class ButtonHandler : MonoBehaviour
{
    public int selectedButton = 0;
    public Image[] buttonList;

    void Start()
    {
        buttonList = GetComponentsInChildren<Image>();
        buttonList[0].color = Color.yellow;
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.LeftArrow))
        {
            MoveToNextButton();
        }
        else if(Input.GetKeyDown(KeyCode.RightArrow))
        {
            MoveToPreviousButton();
        }

        if(Input.GetKeyDown(KeyCode.Space))
        {
            this.BroadcastMessage(buttonList[selectedButton].name + "Action");
        }
    }

    /// <summary>Identical to first example.</summary>
    void MoveToNextButton() {...}

    /// <summary>Identical to first example.</summary>
    void MoveToPreviousButton() {...}

    /// <summary>Will call when user selects "PlayButton".</summary>
    void PlayButtonAction() {...}

    /// <summary>Will call when user selects "OptionsButton"</summary>
    void OptionsButtonAction() {...}
}

1 It should be addressed that these are only requirements due to the way I have implemented it in the example. I feel this is the most suitable implementation for the context.

\$\endgroup\$
2
\$\begingroup\$

so here are quick setup that doesn't need additional programming and everyone (like designers) can change it for the sake of project:

  • first of all OnClick event will call whether you are touching or clicking or pressing a key on the keyboard (EventSystem will look at 'Submit' key in the input manager for that)
  • second, the first one explained everything. just set the 'Cancel' and 'Submit' button in the input manager (It is set up by default) you can use 'First Selected' in the EventSystem to select a button by default and that's pretty much it, use your submit key to press the button and OnClick event to call your events
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .