Sign in to follow this  
ic0de

Help with my input sytem

Recommended Posts

I'm working on overhauling some of the poorly thought out systems in my game. Right now I decided to work on the input system. What I thought I might do is have a buffer (an std::vector) full of abstract actions with some extra data and a timestamp. The player object will then process this buffer and execute the actions accordingly. The advantage of this system is twofold, firstly it abstracts the movement code from the input device (allowing multiple input devices) and second it makes it much easier to record or script inputs. So without further ado here is the interface I created:

#ifndef INPUT_H
#define INPUT_H

class input
{
	public:

	enum 
	{	
		DO_NOTHING = 0x00,

		WALK_FORWARD,
		WALK_BACKWARD,
		WALK_RIGHT,
		WALK_LEFT,
		WALK_ANALOG,

		SPRINT_ENABLE,
		SPRINT_DISABLE,
		JUMP,

		SHOOT,
		ZOOM_VIEW,
		ROTATE_VIEW,

		GRAB_OBJECT		

	};

	input();
	input(unsigned char i, vec d); //constructs a piece of input using an instruction and 3 floats of data, the timestamp is applied automatically
	input(unsigned char i); //constructs a piece of input using an instruction, the timestamp is applied automatically
	~input();

	void fill(unsigned char i, vec d); //constructs a piece of input using an instruction and 3 floats of data, the timestamp is applied automatically
	void fill(unsigned char i); //constructs a piece of input using an instruction, the timestamp is applied automatically

	vec data;
	unsigned char instruction;
	unsigned long long timeStamp;
};

class inputBuffer
{
	std::vector<input>* buffer;

	public:
	
	inputBuffer();
	~inputBuffer();

	void add(input a); //add some input to the bottom of the buffer
	input readTop(); //read from the top of the buffer
	input consumeTop(); //read from the top of the buffer and remove it from the buffer
	void clear(); //clear the buffer
	size_t size(); //get the number of actions in the buffer
};

#endif

Now most of the code is already implemented but I have a little trouble grasping two parts of it: the gathering of the data and what to do with the data at the end of each cycle. The problem with gathering the input is that currently I poll the input which is fine but it means the timestamp will only record when the input was polled rather than when the input event actually occurred. The second problem is that once the input gathering is done and it is time to execute the buffer do I execute the whole buffer or just part of it? Do I clear the buffer before gathering another cycle of input or do I just push back everything else in the buffer? Is anyone able offer advice to help me refine my design?

Edited by ic0de

Share this post


Link to post
Share on other sites

Is your loop progressing at a known pace or just as fast as possible?

If it is progressing as fast as possible, I highly recommend setting a minimum time before the next loop through is done. 16ms is a good time since this means 60fps, which is the refresh rate of almost all monitors.

Share this post


Link to post
Share on other sites

Is your loop progressing at a known pace or just as fast as possible?

If it is progressing as fast as possible, I highly recommend setting a minimum time before the next loop through is done. 16ms is a good time since this means 60fps, which is the refresh rate of almost all monitors.

 

Good idea but it would seem to defeat the purpose of timestamping my inputs in that case.

Share this post


Link to post
Share on other sites

Is your loop progressing at a known pace or just as fast as possible?

If it is progressing as fast as possible, I highly recommend setting a minimum time before the next loop through is done. 16ms is a good time since this means 60fps, which is the refresh rate of almost all monitors.

 

Good idea but it would seem to defeat the purpose of timestamping my inputs in that case.


Which is exactly the point.

Share this post


Link to post
Share on other sites
I was curious at first why you've decided to time stamp when input is recieved , but then I read your post again and noticed that you mentioned scripting. Are you keeping those time stamps because you want to add cut-scenes or replays where the inputs are simulated? Also, are you polling your inputs once every frame in the main loop or is it happening in a thread? I'd just like more information to understand your decision making.

Share this post


Link to post
Share on other sites

Which is exactly the point.

I was curious at first why you've decided to time stamp when input is recieved , but then I read your post again and noticed that you mentioned scripting. Are you keeping those time stamps because you want to add cut-scenes or replays where the inputs are simulated? Also, are you polling your inputs once every frame in the main loop or is it happening in a thread? I'd just like more information to understand your decision making.

Time-stamping is necessary for robust input handling and synchronization with the game simulation.
See this reply.

 

Now most of the code is already implemented but I have a little trouble grasping two parts of it: the gathering of the data and what to do with the data at the end of each cycle. The problem with gathering the input is that currently I poll the input which is fine but it means the timestamp will only record when the input was polled rather than when the input event actually occurred.

In the link above I explain how to handle this problem.
The input must be on a different thread from the rest of your game to avoid this problem.

The second problem is that once the input gathering is done and it is time to execute the buffer do I execute the whole buffer or just part of it? Do I clear the buffer before gathering another cycle of input or do I just push back everything else in the buffer? Is anyone able offer advice to help me refine my design?

Fully explained in the link above.
You will eat the part of the input buffer that occurred during the same logical timeslice you want to simulate.
In a fixed-time simulation, you may simulate only 30 milliseconds of game logic at a time, for example. So you eat 30 milliseconds of the input buffer each time.
This is why the timestamp is so important, and it must be synchronized with your game’s clock so that it eats just the right amount of input.


L. Spiro

Share this post


Link to post
Share on other sites

Read this if you haven't already.

Time-stamping is necessary for robust input handling and synchronization with the game simulation.
See this reply.

 

So I read both links and I was just wondering if someone could help clarify a few things.

 

1. The first approach runs in a single thread which interrupted when a key is pressed or is another thread is started?

 

2. The second approach runs in two threads the second of which gathers input. This seems to favor a polling approach as opposed to a callback. right?

 

3. The two approaches accomplish essentially the same thing but the second one just uses multithreading

 

4. I use SDL for input handling and it seems to have limitations that prevent the use of both systems. Firstly it has no callbacks to my knowledge. Secondly as far as I know input can only be gathered in the main thread which as also where rendering must take place. Does anyone know any workarounds for this? or has this changed in a recent release?

Edited by ic0de

Share this post


Link to post
Share on other sites

2. The second approach runs in two threads the second of which gathers input. This seems to favor a polling approach as opposed to a callback. right?

No, it listens to WM_* Windows event messages. Polling means failure to catch some inputs, which is never acceptable.

3. The two approaches accomplish essentially the same thing but the second one just uses multithreading

Not really. His is more high-level and details the overall structure you should employ whereas mine is focused more on the low-level starting-point implementation.
Basically you would start with an implementation such as mine for gathering inputs on the main thread, unhindered by the game/render thread(s), and then use that as a low-level start to creating the higher-level system he describes.

4. I use SDL for input handling and it seems to have limitations that prevent the use of both systems. Firstly it has no callbacks to my knowledge. Secondly as far as I know input can only be gathered in the main thread which as also where rendering must take place. Does anyone know any workarounds for this? or has this changed in a recent release?

I don’t know about SDL specifically, but generally rendering API’s do not require that. They require that rendering be done on one thread, not necessarily the main thread.
In either case, input will be gathered on the thread that created the window since that is the only way to get WM_* messages.

I would verify that you have understood the limitations of SDL because this is a common misunderstanding among all graphics API’s.


If you verify and find out that you are correct and that rendering must be on the same thread as input, you are basically screwed as far as getting smooth and reliable input until you drop SDL.
Dropping SDL would be my main recommendation, but you may be able to work around it with SetWindowsHook() or SetWindowsHookEx() and bypass the SDL method of handling recognized keyboard and mouse input (while letting it handle unrecognized keyboard input so it can continue to do its thing with maximizing/resizing windows, closing the game on Alf-F4, etc.)


L. Spiro Edited by L. Spiro

Share this post


Link to post
Share on other sites

SDL requires rendering in the thread that created the window, yeah (not sure if it must be the main thread - may be the case for mobile). The limitation isn't really with SDL but with the underlying operating systems (I know Windows at least requires you to read the window events from the same thread that created the window - I have absolutely no idea why, it was like this since the 16-bit era which was cooperative tasking). With input events being handled through the window (keyboard and mouse at least, joystick is generally handled elsewhere in operating systems), there really isn't much of an option.

 

Rendering being in the same thread also has to do with the underlying APIs. I don't know about Direct3D, but OpenGL requires the context to be the currently active one in the thread. OpenGL does allow enabling the context in a different thread and rendering there (at least on Windows), so I'm not sure if there's any additional limitation there or just a feature missing in SDL. It probably isn't safe though and I'm not sure if it'd work on mobile either (for the same reason as the events).

 

So yeah, there isn't much SDL can do. You can tell it to poll the events in the main thread and then pump the events from elsewhere, but otherwise there isn't much else that can be done about it.

Share this post


Link to post
Share on other sites

OpenGL does allow enabling the context in a different thread and rendering there (at least on Windows), so I'm not sure if there's any additional limitation there or just a feature missing in SDL. It probably isn't safe though and I'm not sure if it'd work on mobile either (for the same reason as the events).

No devices have such a limitation. From the sounds of it it is nothing more than an SDL thing.

I would drop SDL, personally. No telling how many work-arounds will become necessary in the future.


L. Spiro

Share this post


Link to post
Share on other sites

If you verify and find out that you are correct and that rendering must be on the same thread as input, you are basically screwed as far as getting smooth and reliable input until you drop SDL.
Dropping SDL would be my main recommendation, but you may be able to work around it with SetWindowsHook() or SetWindowsHookEx() and bypass the SDL method of handling recognized keyboard and mouse input (while letting it handle unrecognized keyboard input so it can continue to do its thing with maximizing/resizing windows, closing the game on Alf-F4, etc.)

 

Normally that's what I would do but dropping SDL would be catastrophic to the portability of my code. Right now my game is a unified codebase that compiles directly under a variety of operating systems (so far only tried it with Windows and Linux). For now I think I'll finish implementing this using SDL's event driven framework as opposed to reading from the keyboard buffer, this should be fine for now so that I can finish implementing the interface and work on other stuff until either I come up with a more novel solution or the SDL api changes.

 

Oh and I'm rendering with OpenGL if anyone was wondering and I'm using the 2.0 version of SDL.

Edited by ic0de

Share this post


Link to post
Share on other sites

No, it listens to WM_* Windows event messages. Polling means...

Windows messages only arrive when you poll the Windows message queue though, so unless you have an extremely high polling rate (>100Hz), your timestamps are going to be wrong anyway, right?

Share this post


Link to post
Share on other sites


No, it listens to WM_* Windows event messages. Polling means...

Windows messages only arrive when you poll the Windows message queue though, so unless you have an extremely high polling rate (>100Hz), your timestamps are going to be wrong anyway, right?


In a basic game loop that most people use at the start of their learning experience you basically peek into the Windows message buffer and either eat the message or draw the frame.
Unless you purposely limit your framerate with sleeps it likely will be 100 Hz or more, or at least 60 Hz.

Of course the speed at which you can poll depends on your game speed which is why I suggest to move that to another thread and poll for input on the same thread that created the window.
This would be done with WaitMessage(), which will keep the thread on a low priority until an input has arrived and should immediately awaken the thread once it has. You should be able to keep the input thread on the highest priority as well since inside WaitMessage() it shouldn’t just be sleeping but in an “event-waiting” state, so it won’t be hogging the CPU but also won’t get drowned out by the other threads when an event does occur and it awakens.

In this case you aren’t really polling. You should be able to get all events within a millisecond of accuracy.


But there is still another option on Windows since you can get message timestamps (forgot the function).
Though it is tricky to synchronize with your game clock, which you would need to do for smooth integration.


L. Spiro

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this