Saturday, September 21, 2013

The Input System

Two weeks went by without a post, and i've been busy.
I've been busy playing Borderlands 2, and brainstorming and implementing my Input System.

I've been thinking on how to implement this system in a way to allow more than two types of devices (mouse and keyboard). There was a good question somewhere on the web that made me think about this: "What if i decided to support a gamepad? How would that change my code?"

So i set to work, and after some google-fu sessions decided on the following approach.

I changed the code a bit to allow the Window to accept listeners which want to listen to all OS messages. Then i registered my InputEngine to the Window, so it gets the messages.

When a message is received, this invokes handle() on the InputEngine, which in turn calls handle() for every device that has been created.

The devices are a way to translate the OS specific messages to my own, device specific, events. MouseDevice, KeyboardDevice, GamepadDevice, TouchscreenDevice, etc, are very platform specific in how they work. Each device has a chance to translate the OS message to a specific event, and in theory, only one device should be able to do that. If the event is successfully mapped, the event is stored in an event queue for later use.

So now that i have a sequence of device specific events, i need to translate them to game specific events. Think of this as "When the user presses <some key>, his character should run forward". So basically, i need to translate the device specific event into an intent (which in this case would be a RUN intent). When it's time to handle input in the game loop, the InputEngine goes through all of the device specific events, and gives them to a context dependent intent generator (name taken from this SO thread).

The intent generator is a sequence of sets of functions that try to map the events to intents, with a specific priority. For instance, in Assassins Creed games, when the player presses and holds the right mouse button, the context changes so all of the buttons map to High Profile actions. When RMB is let go, the context is popped off, and the buttons map back to low profile actions.

So when all device events are processed, the result is a list/sequence of intents (if there was anything mapped to those device specific events) which is returned to the Input Action (my whole engine is based on these actions). The Input Action then traverses all entities that are marked as input controllable, and gives them a chance to execute every intent. If any entity is not capable of executing some intent, it simply doesn't, and the loop goes on.

So this is my input system. I still haven't fully implemented it (i'm currently working on the intent generator), and it took me a while to reach a consensus with myself on how to do it. I tried a couple of different approaches, but they weren't as flexible and/or layered as this one.

No comments:

Post a Comment