Jump to content

  • Log In with Google      Sign In   
  • Create Account

Design of input system


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
26 replies to this topic

#1 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 01:00 AM

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!

Sponsor:

#2 Gage64   Members   -  Reputation: 1227

Like
0Likes
Like

Posted 09 July 2010 - 01:26 AM

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?

#3 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 01:38 AM

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.

#4 Gage64   Members   -  Reputation: 1227

Like
0Likes
Like

Posted 09 July 2010 - 01:59 AM

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.



#5 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 02:06 AM

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

Other suggestions are welcomed.

#6 haegarr   Crossbones+   -  Reputation: 4581

Like
0Likes
Like

Posted 09 July 2010 - 02:19 AM

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.

#7 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 02:27 AM

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.

#8 android_808   Members   -  Reputation: 152

Like
0Likes
Like

Posted 09 July 2010 - 02:41 AM

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]

#9 chronozphere   Members   -  Reputation: 142

Like
0Likes
Like

Posted 09 July 2010 - 03:08 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




#10 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 03:14 AM

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?


#11 Gage64   Members   -  Reputation: 1227

Like
0Likes
Like

Posted 09 July 2010 - 03:47 AM

Quote:
Original post by s.kwee
Yes but in this case every state needs to know about the objects in it. A bit ugly IMHO.


I'm not sure what you mean. Can you explain this, and maybe give an example?

#12 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 03:54 AM

Quote:
Original post by Gage64
I would probably put this code in the InGameState class.

Well, to make it clear we need to define State. If state is what responsible for creating UI, entities, scene and etc (directly or indirectly via appropriate interfaces) then what you said is logical, cause State knows what is Player.

But if State is not responsible for creating Player entity then State shouldn't know about Player entity existence therefore cannot manage it (via player->shoot() or etc).

I hope its more clear now, and to be honest I start to thing that you are right at some point, cause State should create the objects (IMHO) therefore it knows about their existence.

#13 ApochPiQ   Moderators   -  Reputation: 16391

Like
0Likes
Like

Posted 09 July 2010 - 04:07 AM

My solution is pretty straightforward:

  • Define a set of contexts in which actions, states, and values are available. Every context will have different valid inputs. Your menu context may support an ACTION_SCROLL_UP, for example, whereas the main game has ACTION_JUMP, or whatever.

  • Define a mapper which converts raw input forms into mapped actions, states, and values. The mapper controls what action is triggered when you press and release the Enter key, or what state is active when the up arrow key is held down, or what value is set when the mouse is moved. Every context will have exactly one corresponding mapper.

  • Every input tick, your low-level code should pass its input data into the currently active mapper. The mapper that is used depends on which context is currently set. Note that you might want to have a stack of contexts versus just one always-active context, depending on how sophisticated your game input needs to be.

  • Game logic then can use callbacks (for actions) or polling (for states and values) to interpret player input into some kind of internal activity.


#14 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 04:14 AM

ApochPiQ
This sounds like the best solution, and I think I'll go with it.
Thanks for help!

More comments are welcomed!

#15 android_808   Members   -  Reputation: 152

Like
0Likes
Like

Posted 09 July 2010 - 04:33 AM

Like I've put above all my events go through the current game state. A bit of forward thinking that it may or may not be useful for non-interactive animated sequences. Thinking on the line of setting a flag that discards all events except one key press to skip the sequence.

chronozphere's Bind and ApochPIQ's mapper suggestions have also provided me with some ideas of how to improve my set up. Thanks guys

#16 Lloydg   Members   -  Reputation: 114

Like
0Likes
Like

Posted 09 July 2010 - 04:44 AM

what about like this?

input.bind("player_move_forward", KEY_W);

if (input.keydown("player_move_forward"))player.move_forward(dt);


dont know if this helps.. but this is just one way u could do it

#17 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 04:46 AM

Lloydg its similar to what chronozphere suggested.
Its a nice way to group events (like exit on esc or on q buttons) and easier to read and to work with, but does not answer my question.

#18 Lloydg   Members   -  Reputation: 114

Like
0Likes
Like

Posted 09 July 2010 - 04:56 AM

well if you question is about how to get events?

U could do it 3 ways that i know of.
With a library like SDL using SDL_event and polling for events

Or u could use the WindowsAPI win a callback function with the windows message loop.

Or u could manually make the events from just checking key states in an array.


If u want any examples of any of these, just let me know..

maybe this is what u wanted?

#19 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 05:01 AM

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?

#20 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 09 July 2010 - 05:59 AM

Im sorry but
ApochPiQ
I must admit you solution is so simple yet so genius! Now when I'm coding this, its so nice and so clean.

Application holds GameStateManager. GameStateManager manages IGameState. Each IGameState creates what it needs (menus, units, texts etc) and then application calls update and render on GameStateManager that simply calls the update and render of the topmost IGameState. When state is poped the previous state is restored (if any) and if no previous state then GameStateManager notifies the application its time to exit. So brilliant :)




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS