Engine Architecture #1 Apr 22, 2007
Game engine architecture can be a hard subject to write about. There is no universal best engine design, since the best methods will vary significantly depending on the goals of the engine ( and individual programmers. ) Also, although I try to have some familiarity with the code design of engines when possible, you should realize that my qualification to write this is mainly based on one engine. Because this is meant to be article length, I won't go into great detail, or mention more advanced techniques that are are helpful to only certain types of game engine creation. Instead I'll start with an overview to be filled in with links to existing or future articles.
Starting Ideas
One thing to keep in mind is that reducing the chance for bugs is very important. Creating a 3d engine is a complicated task, and there are bound to be mistakes along the way. One of the best ways to gain time is write code that is as bug-free as possible. I don't mean that speed should be ignored, but remember that clear/simple code doesn't always mean slower code, and usually only a small amount of code is speed critical.
You might want to start thinking about your code architecture before actually implementing anything. One way to do this is to write example code of how you want the class to be able to be used. Or similarily, you could start with an empty class, then add new functions as necessary when they are called in other code. ( I like this method. The downsides are changing headers a lot can mean slower compile times once your project grows big, and also that there situations possible in production where you must anticipate use in advance. ) Base Math Classes
If you're writing a 3d engine, there will be plenty of 3d math involved, so it helps to have a good foundation of math classes. I have examples of the most three most standard ( 3D vectors, 4x4 matrices, and quaternions ) in Building Blocks. You may also want 3x4 or 3x3 matrix classes, but these aren't necessary.
You'll want some kind of general bounding class. I use oriented bounding boxes. Oriented bounding-boxes can be overkill for some things, so my bounding class can convert them to axis-aligned bounding boxes or bounding spheres for quicker tests. Some operations show up frequently. One is testing if an object is within a frustum, so I would suggest making some sort of frustum class. Another common one is line intersection with the world. World & Objects
My top level data structure is the game world. It's mostly just a list of objects, and functions to operate on those objects. If you're not sure how you want to structure this, I recommend starting with just an std::vector< ObjectBase* > m_Objects;
This will give you all the functionality you need. Looping through an array doesn't take very long on today's computers. (It is possible, however, if you want massive levels, that you may need to split up the list in various ways for speed.)
It will make your engine a lot easier to work with if it's easy to add new types of objects. Common functionality can live in the object base clase. An important example of a common function is Object->GetBounds(), so all objects can be treated the same by the visibility and culling system. (Additionally I use GetBounds to draw selection boxes in my editor.) The first two object classes I'd suggest are a Mesh Object class, to build the world out of, and a Game Actor Object class. Here I'm using actor to refer to anything that can think and move on its own ( ie. the player, enemies, non-player-characters, beasts. ) // This random code snippet helps break up a giant page of text. ObjectBase *obj = World->NewObject( "Mesh", "SpiralStairs" ); // Type, Asset Name obj->SetPosition( Vec3( 40, 40, 40 ) ); World->RenderObjects( RenderParams ); World->DeleteObject( obj ); Assets
Assets are the building blocks of your world. I suggest making a generalized asset management system, and a base asset class that allows new asset types to be easily plugged in. The two most important types of assets are meshes and textures. Later you may have many more asset types, such as Sounds, Skeletons, Animations, and various others.
Rendering
Games nowadays use graphics cards for 3d-acceleration. For the PC there are two choices to interface with these cards, Direct3D and OpenGL. Either will do, so I'll refrain from making suggestions on which of the two to use in this article. I'll just say that I'm most familiar with OpenGL, but if I were to choose again today, I might go with Direct3D.
A fully functional World->Render function might simply loop through all the objects calling render on each one. However, for speed purposes, you'll want to first call GetBounds on an object, and skip rendering the bounds are outside of the view frustum. Occlusion culling is also important for a real-world game level, where if in single room many other spaces may be rendered even if they're hidden behind the walls. See my Portal Culling article for details. Also described there is the importance of calculating what objects are in a light's influence. Check these article lists for for more on rendering and shaders or lighting and shadows. Gameplay
Advancing the game world can be done by calling a tick function with the number of milliseconds that have passed since the last frame. World->Tick() can call Object->Tick() for each object, although it may also be responsible for advancing systems such as the physics system.Input processing is needed to tell the player actor where to go during its tick function. The standard gaming controls are the mouse and keyboard, which I use the Win32 API to access, wrapped in my own input class. For Joystick & Xbox/XBox360 controller support I go through Direct Input. I don't have any architectual suggestions for input, except that you'll want at least one translation layer from actual key press to the action, so you can allow rebinding of keys, and mapping multiple input configurations or devices onto the same action. Collision Detection is an important part of gameplay. It let's the actors stand on the ground and not move through walls, and detects what shots hit. I started out writing routines for these things myself. Most games nowadays use third-party physics engines though, which offer collision detection routines along with their physics simulation. If you need a physics engine (and which one,) depends on the details of the project. One possibility is PhysX which I believe is free for most uses. Links
3D Editor Programming. Some of the topics I discuss, such as data I/O (serialization), also apply to game programming in general.
|