Advertisement

Keyboard abstraction

Started by June 03, 2019 10:53 PM
6 comments, last by Shaarigan 5 years, 5 months ago

Hey there!

Two weeks ago I started working on a small, open-source project, similar to GLFW. 

I am currently stuck at lining out the proper interface for keyboards. As of now, I am having a static method `keyboard::is_pressed` and a huge enumeration with *some* keys, 
I found on my keyboard. As the name already implies, `keyboard::is_pressed` asynchronously reads the keyboard state and returns true, if the physical key corresponding to the 'virtual key' - passed as argument - is pressed.

This approach, however, feels 'clumsy' or maybe even 'naive' and I feel like there could be a better way to solve this problem, which I am aware of.
So, therefore I am asking here: 
Are there any patterns I am missing out on? Do you have any suggestions on how to solve this problem elegantly?

Regards,
Julian

I write my own game engines because if I'm going to live in buggy crappy filth, I want it to me my own - Ron Gilbert

2 hours ago, Julian Kirsch said:

Do you have any suggestions on how to solve this problem elegantly?

Take a look at the GLFW source code while sashaying. ?

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

Advertisement

Well, depending on your goal, your solution is fine.

SFML uses the same kind of API: https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Keyboard.php

But SDL2 has a much more complex keyboard API: https://wiki.libsdl.org/CategoryKeyboard

 

I have found some issues with the "simple" SFML and your proposed way. Which is most likely also why the SDL2 API is much more complex. In general, there are 3 different types of keyboard input:

  1. Pressing a specific physical key on the keyboard for an action (Use WASD keys for movement, independed of keyboard layout)
  2. Pressing a specific named key on the keyboard for an action (Press "A" to continue, where A is located depends on keyboard layout config)
  3. Typing text, coverting key presses into specific text entry actions. Pressing shift gives nothing, while shift+A is an upper case A. Generally this is more event based.

 

For my home grown game engine, I've build an abstraction called "KeyBinding", which solves a few different things as well, like:

  • Multiple keys bound to the same action
  • Checking if the key was pressed/released this frame/update cycle
  • Assigning joysticks axis to keybinding as well
  • Saving/loading keybindings to a file

https://github.com/daid/SeriousProton2/blob/master/include/sp2/io/keybinding.h

So consider what level of abstraction you are providing, and what your end goal is.

 

Also consider whether you want keys to be pressed if your application has focus or not. Big surprised if you alt-tab to a browser and the game keeps reading your input.

My code uses the HID interface and platform dependent APIs to get the input (not just Keyboard but also Mouse and GamePad). On Windows (and I think Linux too) the HID driver is populating messages to input receivers (a Window). This way what I did was to attach my input system to the main window of my application and have messages be processed as they enter.

This "event driven" input has the advantage to not query the hardware all the time but instead store updates in some kind of input manager. I also not use specific keys or key events directly like the 'A' button but instead generate a 16 bit UID for every key as it provides a state change. The UID is calculated (shifted) from the bist of the HID result like the Usage ID and the key code/mouse button or button/axis ID

Oh, and I checked your code, you seem to use "GetAsyncKeyState" for windows at least, I guess something along the same lines in linux. I've had players manage to press&release a key between 2 frame calls with that kind of setup. So I would recommend against it for things like quick arcade like games. As people get pretty annoyed when they press the jump key and nothing happens.

@Daid Thanks for your input ?
So far, I am planning to keep the level of abstraction low. However, you're 'keybinding' implementation inspired me and will, perhaps, be implemented as a utility on top of the 'low-level' primitives of my library.

Also, thanks for your link to the SDL2 docs. I will probably orient myself to it, when developing the API.

@Shaarigan Before I wrote this post, I was planning to use HID-Input/RAWINPUT but I thought requesting it every time would be better. Is the overhead really this intrusive? 
Also, do you have, by any chance, an idea on how to implement this for linux without requiring sudo? As much as I have gathered so far, I require root privileges to read the raw input data from the fd's.

@fleabay Thanks for the tip! I found its interface to be somewhat similar to SDL's. 

 

I write my own game engines because if I'm going to live in buggy crappy filth, I want it to me my own - Ron Gilbert

Advertisement

I know from someone at a AAA studio that you'll need some kernel functions (userspace) to aquire HID on Linux but he dosen't mentioned anything from root-privileges. I'm not at the point where we are working on the Linux port so I'm a bit out of experience here but there is a reference Lib on GitHub you might want to take a look at.

Generally for gameplay you want as less API calls as possible, so polling the state of an input key every few ms and possibly multiple times per key isn't very performance improving. You 'ld needsome type of caching the poll but this will become very complicated in a multithreaded environment. Because in async processing you'll need to know when a frame ends and the next frame starts to invalidate the state for that certain key.

Going into an event based system, you update the state just when an OS message arrives and anything else is handled by the comes first/serves first principle

This topic is closed to new replies.

Advertisement