Game engine layout

Started by
9 comments, last by jagot 16 years, 4 months ago
Hello! I'm embarking on my journey to create my second game engine, an this time I'd like it to be less game specific and more uncluttered. I know the various bits and pieces needed in the engine, an example listing can be found in this post on GameTutorials:
Quote: - Rendering engine - Sound engine - Some kind of AI system - player control - A camera - Collision and physics - A way to describe game objects - A file loader, or a collection of file loaders Now start to add in some details inside the boxes of the subsstems... - Rendering engine - Texture manager (for loading and storing textures) - A way to handle, store and draw meshes - A way to draw the world (might be the same as the way you draw meshes) - An animation system - A frustum - A particle system - A way to draw 2D stuff and text for the HUD and menus - Sound engine - A way to stream music and speech - A manager for smaller sounds to be loaded and played when you want them - Some kind of AI system - A generic state machine - A way to create states and fit them together - Pathfinding - player control - Some kind of raw input handler (keyboard? Mouse? Joypad?) - A way to turn the raw input into game actions - Collision and physics - A way to represent different collidable shapes (spheres, boxes, cylinders etc) - A way to collide with the world (BSP? Octree?) - A way for objects to collide with each other - A way to work out what's happened when collisions occur - A way to describe game objects - A structure you can fill in a variety of different ways to represent different things (Remember code reuse is the key here, so the game object shouldn't do much in itself, just draw together bits of other things) - A hierarchy? A group of hierarchies? Something totally different? - A manager to look after all the entities - A file loader, or a collection of file loaders - A common interface? - A collection of different loaders, one for textures, one for meshes, one for the world maybe, one for sounds, one for data files... - A memory manager
What I want to know is how I should tie this together. I know that the different subsystems should be oblivious of each other, and that they should be interconnected by an event system. I thought this system up:


class Listener{
	boost::function<void ()> function;
public:
	void call(){
		function();
	}
	
	setFunction(boost::function<void ()> inFunction){
		function=inFunction;
	}
}

std::list<int> messageQueue;
std::vector<Listener> listeners;

int crc(string text){
	//Return CRC of text
}

void sendMessage(string message){
	messageQueue.push_back(crc(message));
}

void checkInput(){
	//A lot of checking going on here
	//...
	//Eg. ESC button pressed
	if(BUTTON=ESC){
		sendEvent("key escape pressed");
	}
	//...
}

void pumpMessages(){
	//While there's still something in the message queue,
	//process it
	while(messageQueue.size()){
		//If there's a listener registered for
		//the message messageQueue[0], call its
		//registered callback
		for(unsigned int i=0; i<listeners.size(), i++){
			if(listeners.getMessage()==messageQueue[0]){
				listeners.exec();
			}
		}
		messageQueue.pop_front();
	}
}

void eventLoop{
	while(running){
		checkInput();
		pumMessages();
	}
}


I suppose eventLoop() should run in a separate thread. Which is best, to use SDL_Thread or pthreads? Comments? What could be done better? Another problem I see in this approach is that the class Listener must contain much non-general information. Suppose I have a GUI with widgets. A button would want to register a Listener for mouseclicks, but only if they occurred on the button itself. Either I'd let the button react on every mouseclick, and then test if it was over the button itself. I would much rather connect Listener::function to Button::click(), which of course only should be triggered when the mouse cursor is above the button. But then the event system must be aware of the buttons dimension, maybe by passing the button boundaries to the listener. That wouldn't be nice now, would it? Another thing that I can't get a grip on is how everything should be encapsulated. Should every subsystem be implemented as a class? Running in its on thread? Wouldn't it seem silly to implement classes which only will be instantiated once during execution (one event-handler, etc.)? I need some ideas how they should be structured and derived from each other. I tried one method where I would place everything as independent functions in different namespaces like, game::graphics, game::sound, game::gui, game::events, you get the idea. But I didn't really like that solution. Cheers, Stefanos Carlström
Advertisement
I personally am not an engine guru by any means. While I was reading your post, I was about to suggest using namespaces for the different engine components, until you mentioned disliking that solution. Singletons perhaps? As far as events are concerned, I'm very fond of functors as a solution. If you haven't heard of that, functors are simply classes that encapsulate a function. This allows you to pass functions as an argument.

When an event occurs, the assigned functor will be invoked. This can be used for a wide range of problems, such as what AI type a character will use, to what a specific button should do when clicked. The only issue I've run into is that the functors have to inherit from a generic type, making arguments to a functor a tricky proposition. You could make a generic structure that can be filled with an optional field of values, but that feels a little sloppy to me.

Good luck with your engine.
In fact I've already decided on using functors, via the boost library to make life simpler:

class Listener{	boost::function<void ()> function;public:	void call(){		function();	}		setFunction(boost::function<void ()> inFunction){		function=inFunction;	}}


setFunction is called like so, from the class where the function to be called resides:
setFunction(boost::bind(&MyClass::callbackFunction, this));


My main problem though is layout of all subsystems, what should be classes and what should not, inheritance and so on. Perhaps someone could post an example diagram (UML or something).

Cheers
Maybe looking at the architecture diagram and feature list for the C4 engine will give you some ideas.
Yeah, that's a nice picture, for sure. But that's more the general ideas, flow and structure, the design of the engine. My question is how I implement the various parts.

Thanks though, the picture can be used as a source of inspiration.

I've found two books that may contain some answers to my questions, both written by David H. Eberly:
* 3D Game Engine Architecture (said to be better than 3D Game Engine Design 1st Ed.)
* 3D Game Engine Design 2nd Ed.

Which one is better?/Which one do you recommend?

Cheers
I have not read the second book, but the first one you mention seemed to me to be more of a reference for his game engine than a book explaining game engine architecture for use in your own engine. Unless reading about someone else's game engine architecture is useful to you (it would at least give you ideas and inspiration as to how you could do things, but if it's worth it to buy the book... thats entirely up to you).
Quote:Original post by issch
I have not read the second book, but the first one you mention seemed to me to be more of a reference for his game engine than a book explaining game engine architecture for use in your own engine. Unless reading about someone else's game engine architecture is useful to you (it would at least give you ideas and inspiration as to how you could do things, but if it's worth it to buy the book... thats entirely up to you).


I'll second that. A very thorough reference though.
Here you can download chapter 3 from 3D Game Engine Architecture, which is one of the main chapters.
Just a few assorted thoughts:

  • Don't be tempted to use Solution X just because it sounds cool, or someone says it might be a reasonable idea. This is a very dangerous habit to fall into. You should only use a particular solution if you have a clear idea of why it solves your design problems effectively, and why it is superior to other alternatives.

  • In particular, I'm thinking of threads and singletons. Seriously, singletons? What makes those an effective option in any way? Just because it comes from the infamous Design Patterns list and sounds fancy doesn't mean it's magic. And if this is just your second engine, threading is definitely some dark voodoo that you don't want to have to deal with. Trust me, the headaches are severe, and you don't need it.

  • Something you may find very useful is to build each module via unit tests. For example, start out with the audio component, and just focus on accomplishing a few things like playing a looping music track, mixing a few sound effects, volume controls, and so on. Just have your main() function test out each of these features in order. Once that's done, move on to, say, rendering. This forces you to keep each system separate, while guaranteeing that each feature works the way you need it to. When you have several systems finished, it should be pretty easy to see how to bind them together.

  • Don't worry about the event system or any cross-module communication just yet. Build that in once the actual subsystems are designed and at least partially functional. It will be much easier to see what needs to be done at that point. Most of the events stuff you've got now is premature.

  • Don't be afraid to rearrange code and adjust how it interacts as you go along. It's not uncommon to get done building a block of code and then realize that you can make it simpler in some way; don't necessarily worry too much about getting every possible simplification up front.


Hope that's helpful!

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

Avoid trying to design everything up front. I believe a game is a complex enough system that you can't foresee how everything will fit together in your code. And unless you've developed a few non-trivial games and saw what went right and what went wrong in your designs for those, your intuition won't be good enough to approximate the best way to fit everything together the first time around.

Instead, take an incremental approach where you make some well-defined milestones, then you break the first one down into a series of small and independent tasks that you can use to build up to the completion of that milestone. Eventually as time goes on, if you're not jumping too far ahead of yourself, you should be refactoring your code as you start developing a sense of how things ought to fit together in your overall design.

By working on independent tasks and refactoring often, you ought to see a more natural design emerge from your code that keeps those pieces that should remain separate decoupled as much as possible. Another benefit, as ApochPiQ said, is that it gives you a chance to unit test each piece as you write it, giving you higher assurance that your code will work as you start to integrate it together.

Case in point, when I started writing my game engine, I started off with the very basic task of creating a functioning logger. Then I focused on a simple architecture that would allow me to connect a game loop with a set of (mostly) independent "services", which more or less represent various subsystems like rendering, audio playback, input processing, timing, etc. Then I worked on a graphics service that simple created a basic render window, then on simple input processing that would kill the window when escape key was hit, so on and so forth. Eventually I needed something to handle higher level game state management, so I wrote a little system on top of my existing code to manage a stack of game states. Key point is that each thing I did was a concrete, independent task that I could fit into the overall game when it was done and continue building up from there. Furthermore, I ended up with a set of powerful pieces that were decoupled from one another, thus I could mix and match them as needed.

That said, sometimes you'll miss your mark, so to speak, and will have to go back and rewrite a piece of code. In cases like this, rewriting your code is a good thing. One it forces you to realize why your initial design wasn't as good (so you hopefully learn to avoid it in the future), but rewriting will also improve code quality and avoid future headaches that you would get if just simply tried to "work around" the problem.

For example, when the time came when my requirements necessitated an event system, my initial implementation, which was quite powerful, wouldn't work in the overall vision and design that I had for my engine. So I had to rewrite it, opting for a less fancy and arguably slightly less elegant variation, but it at least fulfilled the requirements I had and worked in the context of my current code base.

Remember, treat code as something that's easily disposable, not something that's set in stone once it's written, and keep your designs such that when (not if) you have to rewrite something, you'll be minimizing the impact you make to your existing code base.

As far as books go, I liked 3D Game Engine Architecture. It's true that it's more a reference on Eberly's engine, but he makes that clear in his introduction that his discussion is biased towards it. The valuable thing you get from his book though (aside from learning one way you can go about writing your engine) is that you get to see why he chose a particular approach, which is a skill you have to develop when you design your own systems from the ground up.

This topic is closed to new replies.

Advertisement