Detecting Sequential Keypresses with the New Input System in Unity 2023
In this article we’ll look at how we can use Unity’s new Input System package to check for sequential keypresses.
We’ll set it up so that pressing the Tab key will start an algorithm that gives the user 8 seconds to type in a code.
And we’ll do this without any text input boxes what so ever!
Think back to the early days when you could push button combos to unlock cheats and you’ll get an idea of what we are doing here.
But this doesn’t have to be used just for cheat functionality.
This can be used with minor modification any time you want to detect a certain string of input.
This could be the foundation for a new bad-word filter if you wanted to do that.
Now, the Input Asset itself does not have this functionality, but we can still use the new “Keyboard.current” functionality provided by the new Input System to get what we need.
Specifically, we’ll be using “Keyboard.current.onTextInput” which replaced the legacy input system’s “Input.inputString” functionality.
The class defines a Device with a set of key Controls defined by the enumeration. The location of individual keys is…
Per Unity’s documentation, it is best practice to use Keyboard.onTextInput for text input in general.
Basically, any time a user would be typing into a text box or something similar.
We won’t go over the nitty gritty of getting an Input Asset setup in this article.
Setting Up the New Unity Input System
For the most up-to-date instructions on any process, it is always best to look directly to the source. These…
Creating the Input Asset for the new Input System in Unity 2021
To create the Input asset, simply right click in the desired folder of the Project pane, then click Create > Input…
If you need a refresher, see the articles above.
For more articles on the new Input System, go to my Medium page and scroll down to around June/July of 2022.
So basically, we have an Input Asset, with an Input Action Map called “Instructor” that has an Input Action called “StartKeySequence” which has been assigned the Tab key as a binding.
Two Classes Working Together
I have two classes that implement this functionality.
The first handles the game input from the input asset in general.
I’ve cropped it down to just the relevant bits.
Let’s call it the InputHandler class.
The other class has a long-winded name for reasons I won’t get into.
You should probably just call it CheckKeypressSequence or something along those lines.
Now, I’ve got these classes attached to the same gameobject as components.
The InputHandler class has a reference variable for the CheckKeypressSequence class called “_checkForInputSequenceKeyboard”.
The InputHandler Class
I’ve left out the using and class variables section, but you know the drill.
Add a using statement to the UnityEnginer.InputSystem and you can infer any class variables (denoted starting with a “_”) as needed.
So, what’s going on here?
To put it briefly, we’ve instantiated or Input Asset as a class variable and enabled the Instructor Action Map.
We then add subscribers/unsubscribers in OnEnable() and OnDisable() which is a good convention to follow any time you use subscribers.
There we subscribe to the Instructor (Action Map) StartKeySequence (Action), specifically the “performed” event.
The OnInstructorStartKeySequencePerformed() method then calls the referenced CheckKeypressSequence class which is stored as a class variable (_checkForInputSequenceKeyboard).
It calls one simply public method from the CheckKeypressSequence class, the “StartCheckingSequence” method.
Essentially when the new Input System’s Input Asset triggers the StartKeySequence Action’s performed event, we need only to tell the CheckKeypressSequence to do its thing.
The CheckKeypressSequence Class
This class has plenty of room for improvement!
That includes List<> functionality and JSON storage of expected sequences.
But in this article, we just have a simple implementation based on one string at the class variable level called “_strSequence”.
We have some simple class variables that track how long should the user be allowed to input the sequence, how long has the user been inputting the current sequence, and of course what is the current sequence input thus far.
I’ve renamed (not shown) _isTriggeringInput to _isTriggeringKeybind for clarification.
Basically, the Keyboard.current.onTextInput catches the initial keybind if it is also a keypress.
We don’t want the initial “Tab” keypress that was assigned in the Input Asset to count as part of our sequence (or cheat code).
The only public variable, “IsCheckingSequence”, is necessary to keep the triggering keybind in the InputAsset from resetting the _strCurrentSequence if it happens to also be part of the valid sequence.
The Start() Method
In Start(), we’ve call a method to hard code the sequence it is looking for.
In a production situation, you’d probably want to do something with JSON here and implement Lists<>.
We also subscribe to the Keyboard.current.onTextInput event.
Probably best to actually put that subscription in OnEnable().
Again, we unsubscribe in OnDisable() just as we did in our InputHandler class.
The OnTextInput() Method
This method is subscribed to the Keyboard.current.onTextInput event.
Any time the user is typing, this event is triggered.
The event passes us the current character that was pressed.
If this happens to be the keybind, we ignore it and return out of the method.
Otherwise, if IsCheckingSequence is true, we know that we are actively listening for an input sequence.
In that case, we add the new character to the previous characters by updating the _strCurrSequence class variable.
We then call the CheckSequences() method which will compare the _strCurrSequence to any predetermined sequence we have in mind.
The SetKeySequence() Method
This is just a quick dirty method for hard coding a sequence to check against.
Put “cheatalpha” as an assignment to _strSequence.
The CheckForPlayerSequences() Method
And last, here we check the current sequence that has been entered against the expected sequence.
In a production or simply to be more robust, we’d have some list comparison functionality here.
But for this demonstration, this will do the trick.