One thing that I've noticed about the topics of GameDev books is that they almost all follow one of two paths:
1. Lets make a game given one tech and make it suit that game (ie. Making RPG Games in Flash 9)
2. Super technical books catering to the professional crowd (ie. Graphics Gems)
Now there's nothing wrong with these two paths but I find that they under serve the middle ground of intermediate to advanced developers looking to make quality games for fun or profit. Everyone says "Don't re-invent the wheel, use existing tech" but then there's no support for those people wanting to learn how to do exactly that. How many beginners can make the leap from making pong in XNA following along in a book to making "Game Next" that leverages an open source renderer, physics engine and audio suite? Of course maybe the market really doesn't exist. Maybe anyone interested in taking different pieces of tech and building something greater than it's parts already has all of the skills necessary. Then again maybe that's what GD.net is for :)
Where I'm going with all of this is that I've decided to try to use this journal to show my approach to taking a bunch of existing technology and making a game out of it. I'm not going to say an engine as that's not my goal. It may end up looking like Engine Lite just because I'm one to go for re-usability more than others but the end goal is still creating a game.
I plan to post my source code as I go along so people can see how things evolve. I don't pretend that this will look like beautiful shippable source code, especially at the start. I am definitely one who doesn't over analyze or over design up front. I follow the iterative approach of adding things when I see that they're needed and refactoring a lot along the way.
I decided to use Ogre 3D as my rendering engine based on the fact that it matched all of my tech requirements that I had listed in my last journal entry.
Step 1: Building and Demos
I always start with new technology by getting it to build locally and looking at all of the demos. Ideally you do this while you're evaluating the tech to see if it meets your needs. Building sounds like a fairly simple task but it can actually be time consuming depending on how well it was ported to your platform and the build steps involved.
Playing with demos means watching all the pretty pictures that output on your screen as well as stepping through code. Ogre has some great online docs that lay out how the engine is designed so I was reading those while working through the demos.
Step 2: Concept objects
There are a number of basic constructs in the Ogre engine that are shown off in the various demos. These include the Root, Scene Managers, Cameras and Frame Listeners. All of them work together to handle the task of rendering a scene. In the various samples all of these objects are put together to show off a feature of the engine, often as members of a generic Application class. While I've never used these specific constructs I've dealt with similar concepts in my previous work with game development. To make them more comfortable for me I started brainstorming on some higher level objects to encapsulate them.
In the past I've always separated core application code from scene management and game logic. I usually do this by having some sort of Application Class and a separate Scene Class so my first task is figuring out what Ogre3D objects would go in which.
What I end up with is the Root, Window and all Resource objects belonging to the application since they are all shared across scenes. This leaves me with the Camera, SceneManager and FrameListener in the Scene object.
Step 3: Porting a sample
Getting a high level idea of what your classes are going to look like is the easy part. Getting those classes together and working is where the real work comes. I tackle this by taking an existing sample and creating the new classes using copy and paste from the sample. Think of it as rebuilding the same sample but re-arranging the code. The toughest part about this is that you typically find yourself in a state where you can't compile or test things until you get everything moved around successfully. It can get a little hairy at times but that's part of the fun of coding. :)
Step 4: Create base classes/interfaces
Once you have a sample working in your new object structure the next step is to figure out how much of that sample code is re-usable and how much really is sample specific. If you're building a DirectX app do you really need an application specific routine to create your Direct3D objects or can you really have a generic method that creates them all for you given a few parameters.
This is also a great time to ask yourself if you really need ultimate flexibility. Yes it would be great to have an initialization routine that handled single and multi-mon games but do you know you're going to need that functionality? If not then hold off. You can always add it in later. Don't over engineer or you'll never get your project off the ground.
The following are the headers that I currently have after this stage in the process. Note that there is no absolutely right or wrong way to organize your classes. As I said don't over engineer and don't be afraid of creating non-optimized code. Odds are that you're going to refactor this code another dozen times in the future so why worry about something that may not be important or disappear later?
IApplication.h - Base Appilcation Class
IApplication(Ogre::String Name, int Width, int Height);
virtual bool Initialize(void)=0;
virtual void LoadResources(void);
virtual void CreateResourceListener(void);
virtual void Go(void);
bool Configure(Ogre::String Name, int Width, int Height);
IScene.h - Base Scene Class
IScene(Ogre::Root *root, Ogre::RenderWindow* win);
virtual void CreateViewports(void)=0;
virtual void CreateScene(void)=0;
virtual void CreateCamera(void)=0;
virtual void CreateFrameListener(void)=0;
virtual void ChooseSceneManager(void);
IFrameListener.h - Base Frame Listener class
class IFrameListener : public Ogre::FrameListener, public Ogre::WindowEventListener
//OIS Input devices
IFrameListener(Ogre::RenderWindow* win, Ogre::Camera* cam, bool bufferedKeys, bool bufferedMouse);
virtual void showDebugOverlay(bool show);
virtual bool frameStarted (const Ogre::FrameEvent &evt);
virtual bool frameRenderingQueued(const Ogre::FrameEvent &evt);
virtual bool frameEnded(const Ogre::FrameEvent& evt);
virtual void windowResized(Ogre::RenderWindow* rw);
virtual void windowClosed(Ogre::RenderWindow* rw);
virtual void UpdateStats(void);
As you may notice I like to add base functionality to my "interfaces". Some developers will call this bad form but this is what works for me. You need to find what works for you and what you feel comfortable with. After all what is more valuable: shipping code that is slightly "bad form" or ISO 9001 compliant code that never gets off of your hard drive because you keep reworking it to death?
The full source can be found here. It builds in XCode on my machine but odds are it's not going to build out of the box for you if you try it. You should be able to get it working on most platforms with a little massaging. The resources can be found in the Ogre3D SDK.