# How should I structure my engine (classes, etc.)?

This topic is 4337 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Well, I posted a topic not too long ago about what I should do next in my engine. Well, I'm going to stay on the graphics aspect first, before I move on to sound, physics, AI, etc. But before I go any further I have a question. How exactly should I structure my engine? I have a window class and a graphics class right now. The window class creates a window, has a 'GetHwnd' function, and things like that. The graphics class initializes a Direct3D device on the window ('graphics->Create(window->GetHwnd());' -- something like that). In an "EmptyProject" application I created, everything works fine -- creating the window, then graphics, then rendering, etc. But what should the WndProc function do? Should I just catch window messages, such as "WM_DESTROY", and things like that, or what? All of the "*.Update()" functions and things like that can be done in the message loop, where I call "Render()". Is there really a better way to do it? Or should this be fine? Also, how do I handle a lost D3D device if the device is stored in my graphics class? Do I just send some sort of message to the application telling it to re-create any resources necessary or what? Thanks in advance, Matt U.

##### Share on other sites
Most of what you mentioned are design decisions that you should make depending on what the goals of your engine are. For example, in my "engine" (I hesitate to call it that ;), I have a Window ABC with concrete classes for different platforms. Similarly, there is a RenderSys ABC, etc. In my engine, the equivalent of the code snippet you showed would be something like:

WindowPtr window = Display::instance()->createWindow();if(!window->open()){    // error!}if(!window->initRenderSys()){    // error!}RenderSys *renderSys = window->renderSys();

The main difference is using factory functions to create the classes and returning base class pointers to them. The Display's createWindow function will return a Win32Window * on Windows, but an X11Window * on Linux/X11. The window's initRenderSys function will automatically handle the passing of the HWND to the D3DRenderSys class's constructor. This means that the above code would compile and work on Linux/X11 or Mac OS X/Cocoa (if I were to go ahead and write those implementations ;)

As for the second part, I tend to see two different styles in terms of the game loop. The first is just to provide abstract objects and let the developer manage the game loop himself, resulting in something like this:

while(window->exists()){    window->processEvents();    // handle input events    // update game logic, physics simulation, animations, etc.    // render the scene}

The second way is to provide more of a framework, where the engine manages the game loop, and uses callback or ABCs to notify you when to run the appropriate code. In its simplest form, it may just call a runFrame function once each frame and only handle the processing of system level events. Or, it may be something more complex where you register tasks with it, and each task is called whenever appropriate (maybe you only want to update certain game logic every 3 seconds, for example).

The choice is yours, really. Personally, I'm a fan of the first as it gives more flexibility, but if they decide to use some sort of schedule/task based system it means they have to write it themselves.

Anyway, that was a quick response as I have to run :)

##### Share on other sites
Have you looked at some example code in the direct 3d library? Most people start from there and modify it to do your bidding. I wouldn't worry too much about getting a good design on the 'windowing' type stuff as that is pretty low level and not ever going to change.

As far as the rendering engine, take a look at some major open source engines and see how they do it, ogre, allegro, etc, there is one really well structured engine I remember looking at, it had a book written using it. I just checked for the site and it appears to be no more magic-software.com

##### Share on other sites
That engine was renamed to Geometric Tools (some trademark legal crap). http://www.geometrictools.com/

But it is an excellent reference, and is based on (or was used as a base for?) Gamebryo, a commercial game engine.

##### Share on other sites
I personally dislike singletons but that's kinda a side point here(seems what penwan is using, I just think they're really bad design and a needless limitation on an engine/whatever). I personally keep systems/managers/interfaces as seperate as possible. I have a WindowsManager, GraphicsManager, ResourceManager, etc. But on top of that (and not required you can make your own) is an EngineManager which dynamically allocates those (it also loads settings and stuff before that). I also have an EventManager which uses global functions to fire off events. It uses an abstract EventHandler class where you override the Handler function (its similar to GetMessage() in Win32). So you can SendEvent() between anything (register a listener and you're set, I don't like firing an event to everything, including things that don't need it, seems wasteful to me, some Event systems do this though your choice). My GraphicsManager knows about (has a pointer too it) WindowsManager but WindowsManager is clueless about any other Managers. What my WndProc does is fire off events... So if it gets a WM_KEYDOWN it SendEvent(WND_KEYDOWN, ...); Then my InputMgr (you can use your own too) just registers a listener for WND_KEYDOWN and waits. This way my InputMgr doesn't care if it's on a Windows/Linux/MacOS box...

Realize this is just how I did it... Everyone else will be slightly different so pick what works best for YOU. There is no truely right or wrong way. Like others have stated, look at open source engines. :-)

Best of luck.

##### Share on other sites
Stop

You're at the point in a projects life where some design will be better than just diving in. So let's dive in... into design!

What the hell do I actually need?

Start with the biggest things. You need games. Preferably, seeing as most of the code doesn't need to change to make different games, you need a game engine. What is a game engine? Why it's an engine that uses the graphics engine, the physics engine, the AI engine (feel free to substitute the word "library" for engine if you want), the sound engine, and the networking engine. The game engine uses those other engines, so those engines should have no idea about the game engine. Do the "subsystem engines" need to know about each other?. Almost definately, good sir. Physics needs to know what 3D scenery and models it's working with, and the sound needs to know how close to things you are, and AI... well, the AI!

Proceed then to the smaller things. Let's focus entirely on these subsystem engines, and how they relate to each other. You wish to start with the graphics, so we will keep that in our periphery. What does the graphics engine need to communicate to other potential engines?. Triangles, and lots of them. Offsets of those triangles, perhaps. Bones too. It needs to communicate which animations it's playing, for the sound library to play appropriate sounds.

Jonathan, that is a lot of work!

Brilliant!

Moving on down

Now, what do all these things run on?. Computers. And each computer can have a different Operating System (OS), or even different architectures. It's at this point you could decide to make an OS interface, but I won't cover that, because it requires more typing. I'll assume you're using Win32. However, interface or none, we still need a wrapper, as bare Win32 is pretty yucky. Are there other things, apart from windowing, that you may want to abstract? Possibly! Safe to say that an OS-wide wrapper is not only pretty easy, but pretty useful too (and conditional-compliation can remove any dynamic-ness of it all, meaning no actual slowdown due to virtual functions).

What does my windowing section of the OS wrapper need to do. Well, for one, provide windowing. But it also needs to handle events, for resizing, closing and maximizing. Tricky! Now, here is a nitty gritty question: Do you have a window manager, or can you find a way to let windows manage themselves? I mean, this is the age of OOP... can they do it themselves? Unfortunately... no. Win32 needs, somewhere, an Update function (whatever). So, how do we make this the least painful thing possible? Probably by only having to call "Update" once, and only once per frame (per window).

The rest is left as an excersize to the reader: How do I allow generic windows (ie, windows which one can derive from and overide methods like "onMouseUp" and friends) to process events/messages? As a bonus question, how do I do it all with one WndProc function?

Oh man, I just realised how long this would be if I kept typing. Maybe tomorrow I'll continue.

What you really need to take away from this is the process of asking relevant questions to software, to iron out the design. It doesn't even matter what language you're using. I haven't talked about singletons, or classes (much), namespaces, etc. Simply "components" of the system, and how they should interact. As you get "lower down", you begin to define those things, but it's always nice to start with at least a rough idea of how things should work.

And to be honest, this is usually about as far as I take Software Engineering. Sequence diagrams? Seriously, who invented those?

• 36
• 15
• 9
• 23
• 10