Help with my input sytem

Started by
12 comments, last by L. Spiro 11 years ago

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?

Advertisement

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.

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.

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.

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.

Learn all about my current projects and watch some of the game development videos that I've made.

Squared Programming Home

New Personal Journal

Read this if you haven't already.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

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?

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

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.

Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

This topic is closed to new replies.

Advertisement