Game Engine part 7

In this part I’ll introduce another engine subsystem – Input, and we’ll look at how an external API can be neatly wrapped in our own engine. I’m also going to make use of some C++11 features, most of which can be approximated by using Boost, but some stuff is just too neat to pass up on. On a related note – most of the new standard language features is not supported by any compiler yet (userdefined literals, variadic templates, initializer lists, and so forth), but the new STL implementation seems pretty complete. This means more algorithms for containers are available, more containers, regular expressions and perhaps most important of all, standardized threading support. Let me illustrate this with something that would be pretty difficult in C++03:

#include 
#include 
#include 

int main() {
	std::array data = { 1, 2, 3, 4, 5 };
	std::vector> pending;

	for (auto& element: data)
		pending.emplace_back(
			std::async([](int& value) {
				return value * value;
			}, element));

	for (auto& ft: pending)
		std::cout << "Element: " << ft.get() << std::endl;

	return 0;
}

Scheduling parallel executiong of an in-place defined function applied to every element of an array and fetching all results in the correct order without even touching actual threads, mutexes and/or locks, effectively under 10 lines of code. Pretty sweet stuff 🙂

But let's get back on track with the game engine, right -- in part 6 you've created a window and you've probably noticed that shutting down doesn't happen quite as nicely as you'd hoped. Let us start by remembering that the engine runs a loop and keeps going until it has been given a termination signal. So we need to make something that sends a termination signal, say, when the escape button is pressed on the keyboard. Also, any user probably expects his program to shut down when its window is closed, so that needs to trigger the termination signal as well.

We hope to end up with something like this:

void operator()(const KeyPressed& kp) {
	if (kp.mKey == KEY_ESCAPE) {
		EventChannel chan;
		chan.broadcast(TerminationEvent());
	}
}

So we need a class that broadcasts keys that are pressed. GLFW provides C-style callback functions for handling keypresses and releases, so let's wrap it in our own class and translate events from GLFW type to C++ structs:

class Keyboard {
public:
	static void glfwKeyboardCallback(int key, int state) {
		static EventChannel chan;

		if (state == GLFW_PRESS)
			chan.broadcast(KeyPressed(key));
	}

	struct KeyPressed {
		int mKey;

		KeyPressed(int key): mKey(key) {}
	};
};

Of course, when pressing key is being broadcast, releasing them should be as well. And while we're at it, let's keep an overview of every key on the keyboard and its current state.

class Keyboard {
public:
	bool mKeyState[GLFW_KEY_LAST]; //true means the key is pressed
	
	Keyboard() {
		for (auto& key: mKeyState)
			key = false;

		EventChannel chan;
		chan.add(this);
		chan.add(this);
	}

	static void glfwKeyboardCallback(int key, int state) {
		static EventChannel chan;

		switch (state) {
		case GLFW_PRESS:
			chan.broadcast(KeyPressed(key));
			break;
		case GLFW_RELEASE:
			chan.broadcast(KeyReleased(key));
			break;
		default:
			std::cout << "Unhandled glfw state: " << state;
		}
	}

	struct KeyPressed {
		int mKey;

		KeyPressed(int key): mKey(key) {}
	};

	struct KeyReleased {
		int mKey;

		KeyReleased(int key): mKey(key) {}
	};

	void operator()(const KeyPressed& kp) {
		assert(kp.mKey < GLFW_LAST);
		mKeyState[kp.mKey] = true;
	}

	void operator()(const KeyReleased& kr) {
		assert(kp.mKey < GLFW_LAST);
		mKeyState[kp.mKey] = false;
	}
};

Okay, looking good. A little snag however -- GLFW requires a fully initialized window before the callbacks can be attached. This calls for some kind of manager-type class, which suggests to me that it should be a System class, so let's get started on an Input System:

class Input: public System {
public:
	Input():
		System("Input", Task::SINGLETHREADED_REPEATING)
	{
		//add a callback when the window creation has finished
		mChan.add(this); 
	}

	~Input() {}

	bool Input::init() {
		return System::init(); 
	}

	void Input::update() {
		//poll-based input devices (joysticks/gamepads) should be updated here
	}

	void Input::shutdown() {
		glfwSetKeyCallback(nullptr);
	}

	void operator()(const WindowCreated& ) {
		glfwSetKeyCallback(&mKeyboard::glfwKeyboardCallback);
	}
};

There you have it. Thanks to our earlier components implementing this has become very clean and concise. You'll note that the update function can be used to query poll-based input devices, making the class mixed callback- and poll-based. Very convenient. It's pretty straightforward to take the GLFW documentation and implement a similar scheme for a mouse and joystick devices. While I'm at it - take a good look at the documentation of any API you use! For example, by default GLFW processes keyboard and mouse events at each call to glfwSwapBuffers (which displays the current frame). If you don't call glfwSwapBuffers, not only will the window stay blank, but your program will also not respond to key and mouse events, unless you're calling glfwPollEvents in a different loop. Also, special measures must be taken if you want to override system keys (alt+tab etc.)... this is typical stuff that can only be found in the documentation; try to pay attention to various special and/or edge cases.

This should be more than enough to get you started, next time I'll get started on modern openGL usage (core shader-based pipeline). See you then!

9 thoughts on “Game Engine part 7

    1. I don’t consider the work done by any means, it’s more of a time/motivation thing… I do feel there’s a need for something like this so my next holidays will probably be spent writing another part or two

      1. I look forward to more tutorials. I’ve been looking for a tutorial on this stuff for a while and so far this is the best I’ve found. I’m actually in the process of currently setting up the launcher for a game me and a friend plan to create.

  1. Do you have any recommendations on handling inter-system ordering dependencies? I’ve been trying to adopt a more general “Engine + System/Module” approach but the inter-system dependency management always kills me.

    For example, if init is called on your Video system before the Input system, the callback will never occur. The input system has an ordering dependency on the Video system.

    Once I start hitting these kinds of issues I realize that just hardcoding it is the better solution – but only because I cannot come up with anything better.

    1. I’ve tried a couple of different approaches, most of which involve declaring which systems have dependencies on other systems. Once you’ve done that, you could either for a DAG and use graph traversal algorithms to figure out an appropriate ordering (this is what for example a compilation chain does) or, alternatively just keep a list of systems that have been initialized and only try to initialize a system if all of its dependencies have been met. This second approach is far simpler to implement and for the small number of systems involved in game engines it should be at least as performant as the DAG approach.

      Hope this helps 🙂

  2. Just an FYI, people are still very much interested in this, and this appears on the first page of google when looking for tutorials, so it would be a shame to leave it unfinished.

    1. Thank you, I’m hoping to continue soon. I think that the tutorials that are up right now are still relevant, the thing I had in mind for the next part (graphics) tends to go out of date pretty quickly. Anyway, I hope I get the time to work on this again.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.