#7 Lina Engine: Input System - Part 1
Good day everyone, so today I will be talking about our input system. So far, we have a console, a core loop and a basic window rendering. So what's next? We could continue to improve our rendering system and start implementing some basic code for shapes, vertices and shaders, however I personally would like to see some action! And by action I mean being able to scan some input from the hardware and act upon it, so that we would have better test scenarios for our upcoming systems.
In order to handle input, we first need to read it from the hardware. SDL comes pretty handy in this manner, since it provides a cross-platform interface for input handling. However, let's say, it is not really advanced. It can give us information about keyboard and mouse states, and it can also provide us events to notify a particular key press or mouse action, even touch input. We will be focusing on the keyboard & mouse for now.
There are two ways we can use input handling; Reading the state of the keyboard & mouse or waiting for events.
Reading Key & Mouse State
Reading the key and mouse state is pretty straightforward with SDL. We call a method and pass a reference into it, and it binds our reference value to the beginning of the array stored for the active states of keys in the memory, as 0 or 1. This way, we can check if a particular key was pressed, since this array order is stored as the SDL enumerations called SDL_Scancode and SDL_Keycode. Reading SDL states looks something like this:
Discard the memcpy and other attribute set-ups for now, it is implemented this way to provide the control-blocks necessary between frame-readings to create methods like GetKeyReleased, GetKeyDown, GetKey etc. The important part is the GetRelativeMouseState, returns delta position data on the mouse motion, GetKeyboardState, binds the memory address to our attribute so that we can check the key array whenever we need and the GetMouseState, gets the current press states of the mouse buttons. It's pretty straight-forward and useful. We implemented these into our Input Engine class, so that now whenever we need to check whether an input is active on this current frame or not, we can call some getter methods and run some logic on these arrays to determine up & down and currently being pressed states of keys & mouse buttons.
Reading key & mouse state directly from SDL is great, because it is accurate and fast. However, it would not suffice to implement only this method for input handling. Because, then whenever we need some input in any class whether it's a low-level engine system or a high-level game class, we need to ask SDL for the key state every single frame and do our own checks whether it was pressed or not. We don't want that. Sometimes, we would require some objects to listen to a key press, and this listening doesn't need to be on the update. In order to achieve this, we implement events.
SDL provides us with some great event polling. It is able to poll events from it's low-end, and notify us through the right API calls. In our core engine, we receive SDL events through it's poll method, and then pass our events into the input engine for processing.
As you can see above, it's pretty straight-forward. We receive events in our core loop, pass it to the input engine to process. Now from now on, the important part comes into play. First the question, why don't we use events whenever we want to check for input but use states too? The answer is the performance problems of events. Events are expensive, eventhough they are provided perfectly through SDL interface, it's common for some delay between multiple events to occur, and event buffer is not reliable for an engine that needs to process and provide accurate inputs every frame. However, events are pretty useful as well. Not only for input events, but through SDL we can also listen to Window events.
Now the state reading works as explained, we store our states in our input engine, and whoever needs them at any time, can knock on our input engine's door and ask for it. With the events, we will apply a different approach. The input engine will hold some listeners on it, and it will fire events whenever it receives one to these listeners. Any object that wants to be notified upon a particular event, will need to register itself to the input engine, then it will receive a callback when that event is fired off from the input engine and act accordingly. Basically, we would have some signals to be fired from anywhere, or in this case, only input engine, then we would have some slots that these signals will need to land. This pattern is called the famous Signals & Slots pattern from Qt. And it will gives us great extendable functionality our event systems.
That wraps it for now, in the upcoming posts, we will take a look at the implementation of Signals & Slots pattern, and also will implement a different type of event handling due to some restrictions of the pattern and the requirements of the event-design we want in Lina Engine. Until then, stay frosty!