Hey all! I’m starting this blog off with a bang.
A few weeks back, as I was working on a virtual keyboard tool for my personal project, I picked some brains on the MonoGame Discord server for some feedback on my progress. Initial reception was positive, and I was challenged to add additional Mouse/GamePad support for accessibility.
Today I’m open sourcing my core functionality. Happy Holidays to the MonoGame Community!
Demo
The initial release allows usage of a keyboard, mouse, and/or gamepad simultaneously.
The beginning
I started off wanting an easy interface to process user input for my MonoGame project. Although it began with just a text box and some key strokes, I quickly realized I hungered for more.
My first idea was to visualize my inputs- thus the virtual keyboard was born. It didn’t do much more than provide visual feedback for my key strokes, but it looked nice and gave me that dopamine hit I had been looking for.
From there, my mind wandered a bit. I realized I had built a nice looking virtual keyboard that didn’t actually function as one. “What if the user is on a mobile device? I should make these on-screen keys functional.”
Development
That droplet of my first brainstorm set me off on a tangent to make the virtual keys clickable. Due to my lack of foresight, this ended up in a sizeable refactor of the codebase. Once I had restabilized the project and mouse input was working, my vision for the project was, in my mind, complete. I had keyboard and mouse input methods working simultaneously. I even added a caret to show you where the next character would be placed!
With my cool new tool, I ran to the MonoGame discord to show it off and elicit feedback. Everyone seemed to think it was sweet. Life was good!…
Then @Aristurtle had to go and remind me about accessibility.
He was right. In my rush to complete a functional virtual keyboard, I had forgotten an important item- interfaces should be accessible. Without accessibility options, a subset of your users would be without a way to interact with that aspect of your game. It could be…((shudder))…
…literally unplayable.
Well, we can’t have that. Challenge accepted. Let’s build in some accessibility.
Accessibility
From that point, I needed to bring the input interfaces full circle. I finished adding full support for mouse-only interaction, and with full keyboard support already available, I stared down the elephant in the room: gamepad support.
My initial idea around gamepad support seemed fairly straightforward- Treat the gamepad as an anchored mouse. We already have the functionality to process mouse interaction, let’s just move a fake Vector2 around to show the currently selected key.
It’s a bold strategy, CRC. Let’s see how it plays out.
It didn’t work out the way I expected. The main issue being my obsession with making the layout of the virtual keyboard look like a real keyboard. Each key functioned independently- they had no context of each other, allowing me to create different layouts with the same key definitions. This forced another, albiet smaller, refactor.
I didn’t necessarily need keys to have context of each other. However, I did need to find a way to move from one key to another (enabled) key. My solution was LINQ and setting an ‘Active’ key.
The code works by grabbing a sublist of all enabled keys in a given direction, then iterates through each to find the closest one to the currently selected key. I’m sure there are many, more optimized ways to do this, but with only so many keys, doing it this way got it working quickly. Due to the variable offsets created when moving between rows (from the varied key size), movement up and down had to be handled slightly different. To find the closest key, the code instead finds a subset above or below the selected key, then applies a squared distance calculation between each keys center. Applying a distance calc solves entering and leaving problem keys, such as the ‘Space’ key.
Smooth Keyboard Input
I did run into a few issues along the way. The primary one being choppy input from the keyboard. I found that if your input speed exceeds the framerate of the game (~60fps) or multiple keys are pressed at once, keys would be dropped as I was only processing one key at a time. This made keyboard input feel awful.
GetPressedKeys() is the heavy lifter here. It gets all pressed keys sequentially. By pulling in all pressed keys at the same time and refactoring my processors to iterate an array, it provides a very natural feel to keyboard input (and doesn’t drop key strokes! Amazing!).
Final Thoughts
I think that’s all I’ve got. Feel free to check out the MiKeys project on my GitHub. If you have any thoughts or questions, feel free to reach out!
0 Comments