Advice on separating Input from Entities

Started by
20 comments, last by Zipster 14 years, 1 month ago
Quote:Original post by Sean_Seanston
Basically, I sent enums like MOVE_UP, MOVE_LEFT, MOVE_RIGHT, MOVE_DOWN to the entity's input function depending on what key was pressed. Obviously, (though not to me when I was thinking of it) the entity needs to know whether or not it's moving in each direction and not just if it happens to now be moving in a new direction. Otherwise you end up going left then pressing up and releasing left but you're now going up and still left.

Have complimentary events such as MOVE_UP_START, MOVE_UP_END, MOVE_RIGHT_START, MOVE_RIGHT_END, etc., and send the start event when a key is pressed, and the end event when a key is released.
Advertisement
struct Input{  Vec2 Movement; //[-1,1] representing joypad stick  bool Jump;  bool Fire;};class Character{public:  void SetInput(const Input& i)   { mInput = i; }  void Update(float dt)  {   //utilize mInput  }private:  Input mInput;};


It's pretty easy to have ai control the entities when they're setup like this.
Quote:Original post by Zipster
Have complimentary events such as MOVE_UP_START, MOVE_UP_END, MOVE_RIGHT_START, MOVE_RIGHT_END, etc., and send the start event when a key is pressed, and the end event when a key is released.


So I tried that, using a keyboard event handler system I had built before instead of checking the state of the keys which I was using before.

I used
MOVE_LEFT_START
MOVE_LEFT_END

etc. and after some messing around it seems to now work fine for a player. I can't imagine any problems with an AI either so that seems good.

One question about the implementation though:
The way the keyboard system works, it has a map of function pointers associated with keys. To make a key perform a function, you put an entry in the table associating keys, a function and whether it's the key going up or down that it should be associated with. In the Window Procedure, it triggers the appropriate entry in the function table depending on what key has been pressed/released.

So, that means I can't exactly do things the way they were done before. Before I had the PlayerEntityController with an input function that checked the status of the keys and took appropriate action every frame, now because of the way the system works I've put the setting of the keyboard input handlers into the initialization function rather than input since there's obviously no need to put in a new entry of the same thing every frame.

Does this sound alright? It makes the controls seem a little distant from the PlayerEntityController class to me. I'm probably worrying about it for no reason though, since it still does control the setting of its associated keys. So what it does is, it sets the W key or whatever to call the input function of its controlled entities with some specific argument, e.g. MOVE_LEFT_BEGIN.

Another important thing though is that after doing this, if I held down 2 keys and moved diagonally then let go, the plane would often keep moving for a little bit before stopping like it should. So I figured there was some delay in processing the key events. I was confused for a while but today I noticed something. My messages in WinMain were handled by this:
//Handle messagesif( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE ) ){	TranslateMessage ( &msg ) ;	DispatchMessage ( &msg );}


So I was curious and changed it to this:
//Handle messageswhile( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE ) ){	TranslateMessage ( &msg ) ;	DispatchMessage ( &msg );}


Now it appears to work perfectly. Should it have always been while? That seems to make more sense to me, the old system looked like it was just handling a single event every frame no matter how many were on the queue. Now it keeps going til none are left which sounds like it should account for the input lag before.
Just want to make sure that I'm not potentially breaking something that I don't fully understand.

Quote:Original post by bzroom
struct Input{  Vec2 Movement; //[-1,1] representing joypad stick  bool Jump;  bool Fire;};class Character{public:  void SetInput(const Input& i)   { mInput = i; }  void Update(float dt)  {   //utilize mInput  }private:  Input mInput;};


It's pretty easy to have ai control the entities when they're setup like this.


Yeah... that's much like passing a structure of the state like I was thinking earlier. I suppose it's a bit like the system of enums only instead of FIRE_BEGIN and FIRE_END you have bool fire or some such. Might be more elegant if the amount of commands gets high. I'll have to see about that.

So I take it that Update takes action based on the contents of mInput, correct? Does that mean that SetInput() is called every frame to fill mInput with a new updated set of variables?

[Edited by - Sean_Seanston on February 21, 2010 2:13:10 PM]
When you trigger the events, it still makes sense to send a little structure along with them that contains information about what triggered the event, if only for debugging and those odd cases where you will need it.

It's a trade-off between the number of unique events and the amount of logic performed outside the input system. For instance, you could have MOVE_LEFT_BEGIN/MOVE_LEFT_END events mapped to 'A' down/up, or you could just have a MOVE_LEFT event mapped to 'A' and send an up/down bit with the event so that game logic can check it. Or you just send raw key information and let the game do everything. It's a balance between shared logic in the input system versus game-specific logic in the entities and controllers. Our engine at work just has MOVE_LEFT/MOVE_RIGHT/etc. events that it sends along with information about the key/button that triggered the event, but this is mainly because our GUI layer requires untranslated events for edit boxes and the like and we don't want to create GAME_H_DOWN/GAME_H_UP events for every single keyboard key. In your case, individual events for everything might work just fine, it's really up to you.
Quote:Original post by Zipster
When you trigger the events, it still makes sense to send a little structure along with them that contains information about what triggered the event, if only for debugging and those odd cases where you will need it.


Alright so... the one global map of keys to functions is ok but I should use it to pass a whole structure instead of just a simple enum? With a pointer to the calling object maybe?

In that case should I just include the enum within a structure and add a pointer to the calling object etc.?
How to handle analogue inputs like mouse & joystic & wheel?
enum TURN_LEFT_73_UNITS ?

/Tyrian
Quote:
In that case should I just include the enum within a structure and add a pointer to the calling object etc.?


struct InputEvent{	int Key;	int Event;	bool Pressed;		//1 - pressed, 0 - released	float Value;};


This is the kind of structure I am using at the moment for passing input to entities. So I have a translator class which converts the key to the actual event (the enum). I keep the key inside the class too for the rare cases it is needed.

the Pressed variable tells whether the key is pressed or released. So that I don't need separate event for releasing the key. Of course, that's up to you whether you want to have MOVE_FORWARD/MOVE_FORWARD_RELEASED events or just MOVE_FORWARD.

The float "Value" is used for input events such as mouse movement or analog joysticks.

I don't see a reason to pass the caller object. Perhaps you could explain why you'd want to do that.

You'll need experiment by yourself what kind of solution suits your needs.

Cheers!
Quote:This is the kind of structure I am using at the moment for passing input to entities. So I have a translator class which converts the key to the actual event (the enum). I keep the key inside the class too for the rare cases it is needed


Hmmm... yeah, that looks pretty good. Seems to make sense. Thanks.

Quote:I don't see a reason to pass the caller object. Perhaps you could explain why you'd want to do that.


It was because of something Zipster said but now that I read it again I'm not sure if I misunderstood when he said:

Quote:Original post by Zipster
When you trigger the events, it still makes sense to send a little structure along with them that contains information about what triggered the event, if only for debugging and those odd cases where you will need it.


I took "what triggered the event" to mean the object that triggered the event, but now I'm not sure if he meant the situation that triggered the event... surely the only thing you'd know for sure would be what key was pressed? Unless he was referring to the situation that caused the AI to trigger that event...
OP:
You could also make a callback class, make player entity inherit from it and make it declare which actions do you need and where to send them.. and for the action mapping thing.. just declare an array of input elements which can be attached to a input reader which, then, changes some variables in that array so you could check for them in the player entity. That's how I do action mapping.
What is the analogue inputs which used in the game.I like to know the leading concern which inventing and engaging in the field.I am interested in game support.
your posts are highly giving the knowledge about the PC games.

This topic is closed to new replies.

Advertisement