Need help with keybind script functionality!

So here’s the context:

I created a keybind script that is intended to function as follows:

When I click on a button in my Controls menu I want to be able to enter a keyboard key or click the mouse and the key/button entered should display on the button’s UI text element. During this key/button input window, I would like all buttons to become non-interactable so accidently clicking on another button while attempting to assign a mouse button to an action doesn’t work. By doing this, I set that appropriately (or atleast I thought) in my code but even though they are non interactable during this time, the additional button is still being clicked! I tried setting a pause in my script before re-enabling all the buttons, but that didn’t seem to work. What am I missing/doing wrong here?!!

using System.Collections;
using System.Linq;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;

public class KeyBindScript : MonoBehaviour {

    public bool AVMenuActive = true;

    public bool GPMenuActive = false;

    public bool ControlsMenuActive = false;

    public Button[] InactiveButtons = new Button[23];

    public Dictionary<string, KeyCode> Keys = new Dictionary<string, KeyCode> ();

    private Dictionary<string, Text> ButTextLookup = new Dictionary<string, Text> ();

    private GameObject currentKey;

    public Text forward, backwards, movleft, movright, leanleft, leanright, run, jump, crouch, interact, attack, clairvoyance, journal, inventory, tasks, watch, quickwheel, pausemenu; 

    void Start ()
    {
        Keys.Add("Forward", KeyCode.W);
        Keys.Add("Backwards", KeyCode.S);
        Keys.Add("MovLeft", KeyCode.A);
        Keys.Add("MovRight", KeyCode.D);
        Keys.Add("LeanLeft", KeyCode.Q);
        Keys.Add("LeanRight", KeyCode.E);
        Keys.Add("Run", KeyCode.LeftShift);
        Keys.Add("Jump", KeyCode.Space);
        Keys.Add("Crouch", KeyCode.LeftControl);
        Keys.Add("Interact", KeyCode.F);
        Keys.Add("Attack", KeyCode.Mouse0);
        Keys.Add("Clairvoyance", KeyCode.Mouse1);
        Keys.Add("Journal", KeyCode.J);
        Keys.Add("Inventory", KeyCode.I);
        Keys.Add("Tasks", KeyCode.X);
        Keys.Add("Watch", KeyCode.C);
        Keys.Add("QuickWheel", KeyCode.Mouse3);
        Keys.Add("PauseMenu", KeyCode.Escape);
        ButTextLookup.Add ("Forward", forward);
        ButTextLookup.Add("Backwards", backwards);
        ButTextLookup.Add ("MovLeft", movleft);
        ButTextLookup.Add ("MovRight", movright);
        ButTextLookup.Add ("LeanLeft", leanleft);
        ButTextLookup.Add ("LeanRight", leanright);
        ButTextLookup.Add ("Run", run);
        ButTextLookup.Add ("Jump", jump);
        ButTextLookup.Add ("Crouch", crouch);
        ButTextLookup.Add ("Interact", interact);
        ButTextLookup.Add ("Attack", attack);
        ButTextLookup.Add ("Clairvoyance", clairvoyance);
        ButTextLookup.Add ("Journal", journal);
        ButTextLookup.Add ("Inventory", inventory);
        ButTextLookup.Add ("Tasks", tasks);
        ButTextLookup.Add ("Watch", watch);
        ButTextLookup.Add ("QuickWheel", quickwheel);
        ButTextLookup.Add ("PauseMenu", pausemenu);

        forward.text = Keys ["Forward"].ToString ();
        backwards.text = Keys ["Backwards"].ToString ();
        movleft.text = Keys ["MovLeft"].ToString ();
        movright.text = Keys ["MovRight"].ToString ();
        leanleft.text = Keys ["LeanLeft"].ToString ();
        leanright.text = Keys ["LeanRight"].ToString ();
        run.text = Keys ["Run"].ToString ();
        jump.text = Keys ["Jump"].ToString ();
        crouch.text = Keys ["Crouch"].ToString ();
        interact.text = Keys ["Interact"].ToString ();
        attack.text = Keys ["Attack"].ToString ();
        clairvoyance.text = Keys ["Clairvoyance"].ToString ();
        journal.text = Keys ["Journal"].ToString ();
        inventory.text = Keys ["Inventory"].ToString ();
        tasks.text = Keys ["Tasks"].ToString ();
        watch.text = Keys ["Watch"].ToString ();
        quickwheel.text = Keys ["QuickWheel"].ToString ();
        pausemenu.text = Keys ["PauseMenu"].ToString ();
    }

    public void EnableAVMenu()
    {
        AVMenuActive = true;
        GPMenuActive = false;
        ControlsMenuActive = false;
    }

    public void EnableGPMenu()
    {
        AVMenuActive = false;
        GPMenuActive = true;
        ControlsMenuActive = false;
    }

    public void EnableControlsMenu()
    {
        AVMenuActive = false;
        GPMenuActive = false;
        ControlsMenuActive = true;
    }
      

    void OnGUI()
    {
        if (currentKey != null) {
            foreach (Button button in InactiveButtons) {
                button.interactable = false;
            }
            Event e = Event.current;
            if (e.isKey | e.isMouse) {
                Keys [currentKey.name] = (e.keyCode);
                foreach (var kvp in Keys.ToList()) {
                    if (kvp.Value == e.keyCode) {
                        if (currentKey.name != kvp.Key) {
                            Keys [kvp.Key] = KeyCode.None;
                            ButTextLookup [kvp.Key].text = "*Unbound*";
                        }
                    }
                }
              
                currentKey.transform.GetChild (0).GetComponent<Text> ().text = e.keyCode.ToString ();
                currentKey = null;

                System.Threading.Thread.Sleep(1000);

                foreach (Button button in InactiveButtons) {
                    button.interactable = true;
                }
            }
          
        }
    }

    public void ChangeKey(GameObject clicked)
    {
            currentKey = clicked;
    }

    public void ResetKeys()
    {
        if (ControlsMenuActive == true) {
            Keys ["Forward"] = KeyCode.W;
            Keys ["Backwards"] = KeyCode.S;
            Keys ["MovLeft"] = KeyCode.A;
            Keys ["MovRight"] = KeyCode.D;
            Keys ["LeanLeft"] = KeyCode.Q;
            Keys ["LeanRight"] = KeyCode.E;
            Keys ["Run"] = KeyCode.LeftShift;
            Keys ["Jump"] = KeyCode.Space;
            Keys ["Crouch"] = KeyCode.LeftControl;
            Keys ["Interact"] = KeyCode.F;
            Keys ["Attack"] = KeyCode.Mouse0;
            Keys ["Clairvoyance"] = KeyCode.Mouse1;
            Keys ["Journal"] = KeyCode.J;
            Keys ["Inventory"] = KeyCode.I;
            Keys ["Tasks"] = KeyCode.X;
            Keys ["Watch"] = KeyCode.C;
            Keys ["QuickWheel"] = KeyCode.Mouse3;
            Keys ["PauseMenu"] = KeyCode.Escape;
            forward.text = Keys ["Forward"].ToString ();
            backwards.text = Keys ["Backwards"].ToString ();
            movleft.text = Keys ["MovLeft"].ToString ();
            movright.text = Keys ["MovRight"].ToString ();
            leanleft.text = Keys ["LeanLeft"].ToString ();
            leanright.text = Keys ["LeanRight"].ToString ();
            run.text = Keys ["Run"].ToString ();
            jump.text = Keys ["Jump"].ToString ();
            crouch.text = Keys ["Crouch"].ToString ();
            interact.text = Keys ["Interact"].ToString ();
            attack.text = Keys ["Attack"].ToString ();
            clairvoyance.text = Keys ["Clairvoyance"].ToString ();
            journal.text = Keys ["Journal"].ToString ();
            inventory.text = Keys ["Inventory"].ToString ();
            tasks.text = Keys ["Tasks"].ToString ();
            watch.text = Keys ["Watch"].ToString ();
            quickwheel.text = Keys ["QuickWheel"].ToString ();
            pausemenu.text = Keys ["PauseMenu"].ToString ();
        }
  
    }
      

    //public void ConsoleOutput()
    //{
        //foreach (KeyValuePair<string, KeyCode> kvp in Keys)
            //Debug.Log ("Key = {0} + Value = {1}" + kvp.Key + kvp.Value);



}

Hello

Why are you using Event.current instead of Input.GetKeyDown?

I believe this is an order of events issue. Input is checked once per frame, outside of the OnGUI loop. So sleeping won’t help you. How about moving disabling and enabling the buttons into the update loop? I haven’t read your code in detail, but this fixes 90% of problems with event ordering and OnGUI.

The Legacy OnGUI system, while pretty much inferior in every other way, is able to listen for any key with Event.current in a way the Input system can’t. To do it with Input you would need to iterate over every single keycode every frame. While its not especially difficult, it isn’t quite as convenient.

How about checking if any key at all is pressed first?
Input.anyKeyDown

Hmmm, interesting. If I changed line 106 in my code to “void update()” (from “void onGUI()”) do you think it would have a negative impact on my code currently housed there?

So I changed my onGUI() function to Update() and am getting this exception:

NullReferenceException: Object reference not set to an instance of an object
KeyBindScript.Update () (at Assets/KeyBindScript.cs:115)

Are you sure you know what you are doing? Using OnGUI is only recommended for advanced users.

Event.current can only be read from OnGUI, so that will give you a problem to start with.

Nah, I’m pretty new to coding. I understand basic concepts, but I’m no whiz.

Have you considered purchasing something like rewired instead?

absolutely, but I wouldn’t learn anything then. Give a man a fish and he eats for a night… Teach a man to fish, and he eats for life.

Its closer to ‘Give a man a fully equipped fishing trawler’ vs ‘Teach a man to fish with his bare hands’.

But if you want the bare hands approach… Here is how I would approach it in pseudocode:

public bool isChangingKeys;

// Call from wherever you initiate the process
public void ChangeKey (){
    isChangingKeys = true;
    // Disable all buttons
}

void DoneChangingKeys(){
    isChangingKeys = false;
    // enable all buttons
}

void OnGUI (){
    if(isChangingKeys){
        Event e = Event.current;
        if (e.isKey){
            // Assign the key with e.keyCode
            DoneChangingKeys();
        }
    }
}

Alternatively you can just overlay a panel across the whole screen.

Another option would be to use a canvas group and disable raycasting (or interactable) on the set of controls not being used.

As for gathering the keys during Update, that can be done by creating a list or array of all of the valid key codes you want to check against. Looping through those in Update, you can check what’s ‘down’.
You can also use the Event.PopEvent, which I think I wrote in another thread for you.

Either way, just make sure the script is disabled while you’re not setting keybinds, so it doesn’t have any work to do :slight_smile:

this didn’t work for disabling the keys or using a transparent panel to cover them. Ill click a button to remap a key, and then click the mouse (near another button) to remap it to a mouse button and the new button has still accepted the click.??