Tuesday, January 21, 2014

Input System Update 3 - Keyboard Working!

Hello Again,

I'm here to announce today that the keyboard part of the input system is now live and working! As I said on the last post, I'm postponing the messaging system, but I am yet to decide how to handle this. So, I am now focusing on the callback approach, where you register a function to be called when a pattern is matched. As of now, the only accepted function form is void(*)(). This means that the function should be void with no arguments (like void setJump(); ).

All of our system's parts are finished for keyboard input, and it is enough to show you how exactly it is supposed to be used, and give you a good idea on what to expect for mouse and thumbstick.

Basically, the keyboard has two possible actions: key up and key down. But mouses and thumbsticks also have movement. This movement adds a whole new complexity to them. It will need to pass along more information, like the pointer position when a click happened. This makes me question the viability of using the same approach I did with the keyboard. The problem is that those controls are special and should be managed as such... I'll probably make a simplified interpreter that could be used to get simple actions (such as single or double clicks) and maybe some specific movement gestures like shaking the pointer. On top of that, the program should still have a pointer to the controller object of the mouse, so it could easily get the pointer position. Of course, an indirect interface for this kind of thing is necessary, in case this controller pointer is no longer on scope or for safer code where you only want to interact with the interpreter.

Back to the keyboard, It is done and functional. Not field-tested, but tested to some extent. This is an example code of how to use the entire system, from the start to the update(); :


This code shows the usage of all the classes mentioned on the last post: GestureInterpreter, Pattern, BasicInput.
This code will detect whenever the escape key is pressed or the W is released and will exit by using the callback. It will also answer with a console output whenever the user hit the ASD keys in sequence within the configured time limits.

The link to download the source code is here: Download. The example contained inside is different, as it tests for slightly more complex patterns (that would not embed the blog very well).
Note: forgive me for the code itself, I will clean it up before anything else, and, specially, separate the useful files from the ones that are still useless.

My planned next steps are:
  • Add gamepad support (no thumbstick here);
  • Create a showcase for this system allowing for Gamepad and Keyboard (minigame);
  • Add as much support for mouse and thumbsticks interpretation as viable;
  • Create the interface to allow a more direct access to both mouse and thumbs.
In the future:
  • Add the polling and messaging systems to the interpreter;
  • Create a Lua interface, so this can be done on a higher (and less wordy) level; and probably
  • add a way to read the event log externally.

I also want to say that if any of you have any feedback it will be appreciated. Thank you all who already sent me messages with suggestions and comments!

Over and Out.

Tuesday, January 14, 2014

Input System Update 2

Hello Again,

I've been working more on our input system and now I'm almost done coding the Gesture Interpreter, functionality-wise, for simple controllers (digital buttons). It will be enough for keyboards and some gamepads. Next step will be polishing, testing and getting rid of all bugs and leaks that certainly are hidden in there. When I am done with this, I'll then move to creating an specialized interpreter for mouse and one for gamepads with thumbsticks.

Like this one.

As of now, the GestureInterpreter has only one working approach, Callbacks. Since it is the most common, and I find it easier to program and test, I decided to add it before the other two: Messaging and the Polling systems. But don't worry, I'm going to implement them as well eventually (Valve time? oh boy, you know nothing).

Overview

The system is simple. You have a GestureInterpreter that receives events from a Controller and translates its keys into internal game codes by using a KeyTranslator. Once translated, the GestureInterpreter compares the last received inputs with all Patterns you created by serializing BasicInputs such as " 'Space' Key Down" or " 'W' Key Up"; and, if it finds a match, it calls that Pattern's registered Callback. Pretty simple isn't it?

The system was designed to stop comparing patterns as soon as it finds a match, but can easily be changed to always compare with every pattern and call all callbacks. I decided to stop at the first match so you know that it will only call a single callback for a single input event.

"Why?", you ask me. Let me illustrate the situation with a simple example: imagine an old-school fighting game. One of the buttons, say 'A', is responsible for the 'punch' action, but the sequence " ↓↘→ A " is responsible for an 'uppercut' action. In addition, the other sequence " ←↙↓↘→ A " unleashes an even stronger 'special attack' action. Notice how all three patterns end with an input event of " 'A' Key Down". If the player presses the correct sequence for the special attack, the GestureInterpreter would then call all three callback functions, for the punch, the uppercut and the special attack.

Moving on, the code basically calls a return true; when it gets the first match. That means we will only call the "higher" pattern's callback function, instead of all three. However, changing this is as simple as moving the return statement to outside the comparison loop and make it return a variable that stores if there was a pattern match or not. This is the function:



The problem with this approach is that it forces us to manually call the updateStep() method until it returns false. Under normal circumstances, it will be one single call, but if we have an special type of controller (say, a gamepad with autofire) or a really (i mean, really) fast tapper playing, it might be a problem. This input system is intended to be initialized and configured once, and then all you need to do is to call the updateStep() function on your interpreter until it is satisfied; it will call all your callbacks with no need for any further steps. Or you can simply call update() once if you don't need the value updateStep() returns.

As of now, the memory cycle is complete. All created events should be deleted on the GestureInterpreter (or by their creators, if the creator's dtor is called before the event got checked out).

This version of the code is an earth field, sometimes you can find some earth between the mines! I haven't made a single test, since I am lazy. But will do; tomorrow.

If you are crazy enough, download it!

Over and Out.