Design of input system

Started by
25 comments, last by theOcelot 13 years, 9 months ago
Hi.
I thinking about how to do an input system.
I am going to use Win32API (anyone got better solution?) to capture the input events (mouse and keyboard, no plan for controllers or joysticks yet).
So from what I understood I should have some array of key states (and mouse state) and every frame I call my input system to check if something is pressed.
Correct me if I'm wrong up to this point.

This works nice for a demo where you don't have different game states. So this brings me to GameStateOperator. GSO (as I call it) is simply a wrapper upon stack DS that holds various GameState objects. The top most GameState in the stack is the current state of the game and the only one who can parse the input events.

Example:
Let there be two GameStates:
1. MainMenuState
2. InGameState

Both states can parse the ESC button press however they acts differently. MainMenuState is the default state that loaded when the game initialized. If current state is InGameState, pressing ESC will pop this state therefore the current state will be MainMenuState and pressing ESC here will quit the application.

This is nice and cool, but lets add entities. Entity (dont yell at me if I define it wrong) as I see it is something that can be present in the scene (be it visible and dynamic like car, visible and static like wall or invisible like trigger). Some entities would like to handle input as well (Player entity for example).

What I tough about is to do an Interface like IKeyboardListener and IMouseListener and everyone who wishes to receive keyboard or mouse events will implements them and registers itself within InputManager.

The same goes for GameState class. It will by default inherit from IKeyboardListener and IMouseListener.
For the input manager to know if the event was parsed withing specific listener, the listener must return either true or false.
Pseudo code:
[source lang=cpp]InputManager::handle(){ foreach(kb in this->mKeyboardListeners){  if(kb->handle(this->kbState)) break; }}

So if the listener returned true, the InputManager knows that that current state was handled and no more listener needs to be called.

This is a brief of how I see it. Somethings were tough about for a long time, others just came into my head right now. It looks like a flexible system for me but I would you to comment on it and show me were I went wrong, if at all.

Thanks!

I would love to change the world, but they won’t give me the source code.

Advertisement
Quote:Original post by s.kwee
What I tough about is to do an Interface like IKeyboardListener and IMouseListener and everyone who wishes to receive keyboard or mouse events will implements them and registers itself within InputManager.


Disclaimer: I don't have a lot of experience with this sort of thing.

I don't think entities should really care about the keyboard and the mouse. A player, for example, should be able to move forward, jump, fire his weapon, etc. It shouldn't worry about what caused him to jump or fire.

Game logic should be decoupled from this kind of low-level stuff (like input devices).

Quote:For the input manager to know if the event was parsed withing specific listener, the listener must return either true or false... So if the listener returned true, the InputManager knows that that current state was handled and no more listener needs to be called.


What if several listeners are interested in the input?
Quote:Disclaimer: I don't have a lot of experience with this sort of thing.

I don't think entities should really care about the keyboard and the mouse. A player, for example, should be able to move forward, jump, fire his weapon, etc. It shouldn't worry about what caused him to jump or fire.

Game logic should be decoupled from this kind of low-level stuff (like input devices).

Then who cares about the input? When you press W who knows that your avatar should move forward?

Quote:
What if several listeners are interested in the input?

Hmm right, I didn't think it well enough... I was confused with single event being parsed (like W key down) therefore no listener should parse this event anymore.

Well what I meant was if W was pressed, its passed to the first listener if the listener knows how to handle W it handles it and no other listeners must handle it. This what I meant.

I would love to change the world, but they won’t give me the source code.

Quote:Original post by s.kwee
Then who cares about the input? When you press W who knows that your avatar should move forward?


I was thinking about something like this (pseudo code, just to demonstrate the idea):

if (keyboard.keyDown(KEY_W))    player.moveForward();if (mouse.pressed(BUTTON_LEFT))    player.shoot();


As you can see, the player is completely decoupled from the input handling code. I would probably put this code in the InGameState class.

Yes but in this case every state needs to know about the objects in it. A bit ugly IMHO.

Other suggestions are welcomed.

I would love to change the world, but they won’t give me the source code.

Gage64's suggestion becomes really valuable if you think of it as conversion from "raw" input to "logical" input. It is a typical method used since many years for user configurations of input: The raw input is a key-press, mouse motion, and so on, that gets converted into logical input like useItemInHand, moveForward, ... This way also the nature of the input device is hidden, and only the capabilities of their elements is used (e.g. whether the left mouse button or the "x" key is used to trigger useItemInHand plays no role, since both these elements are equivalent: 2 states, one of them stable; if interested in: USB HID specification enumerates such things in detail). This way allows to easily add other kinds of input devices later on.

However, it is IMHO correct that the logical input should be descriptive and not implemented directly in a bunch of if-clauses.


Just another thought related to the OP: A stack is not the best structure to manage global game state. Game states build a kind of graph with transitions between them. And those transitions are not always mappable to a FIFO structure.
Hmm sounds logic now. Maybe I need to start coding to see it.
IMHO its a bit violation of encapsulation since State shouldn't know about player entity or etc.

Quote:Just another thought related to the OP: A stack is not the best structure to manage global game state. Game states build a kind of graph with transitions between them. And those transitions are not always mappable to a FIFO structure.

Could you please give an example of a system where FIFO wont work? I can't come up with anything.

I would love to change the world, but they won’t give me the source code.

Quote:Original post by Gage64
Quote:Original post by s.kwee
Then who cares about the input? When you press W who knows that your avatar should move forward?


I was thinking about something like this (pseudo code, just to demonstrate the idea):

*** Source Snippet Removed ***

As you can see, the player is completely decoupled from the input handling code. I would probably put this code in the InGameState class.

Just be careful when it comes to handling held keys. The method you suggest here would work OK. However you decide to do it you need to have a way of keeping track of what keys are held down so as to avoid for example only moving forward one step for every keypress. This could either be in the Input class above as something like a bool array (easier, usually more efficient) or in the 'entities' themselves somehow.

The only other thing is potentially a nested 'if' for walking or running with w pressed.

At the moment I have a listener setup passing events like so:

Game Engine DLLs:
Window::ProcessEvents()
Creates a struct for each received event and passes it to all registered listeners once per frame.

Game::OnEvent(const Event&)
Class acts as core responsible for creation of subsystems. Sends resize events to renderer and sets an exit flag on quit events. Events are not consumed so those and other events (keys, mouse etc.) are sent to the registered GameLogic.

Executable:
GameLogic::OnEvent(const Event&)
A game specific derivative is registered with Game as one of its constructor parameters. More like a game state/screen manager. More than one state can be active at once (overlaid menus etc) like a stack. Event is passed on to each one, starting from the top of the stack until it is consumed. Modal dialog boxes for example would just consume everything.

MainMenuScreen::OnEvent(const Event&)
Basically the same as the source snippet I quoted above, processes input and sends to the relevant entity/ scene node/ animator or whatever.

As you can see there's a lot of passing the buck going on, something I aim to address somehow at a later date. This is only handling Windows (SDL actually) generated messages, no in game GUI related code which has caused even more headaches.



[Edited by - android_808 on July 9, 2010 9:41:28 AM]
This is what I do:

Input.BindKey('quit_game',N3DK_ESCAPE);Input.BindKey('quit_game',N3DK_Q);if (Input.GetKeyState('quit_game') == N3D_PRESS){  //Code to terminate game here...}


As you can see, I can bind multiple keys to a single "slot". Each slot has a specific function in the game, like move left, move right or quit game. You can then retrieve the state of the slot. There are four possible states:

N3D_RELEASED
N3D_PRESS
N3D_PRESSED
N3D_RELEASE

This is really convenient. It allows key-bindings to be customized by the user


chronozphere
It doesn't answer my question: how to handle Entity events? Like player movement? Should player entity implement some interface and register itself as keyboard/mouse listener? Should it be handled inside the current game state?

I would love to change the world, but they won’t give me the source code.

This topic is closed to new replies.

Advertisement