Recommended Posts

I'm just starting on the framework of my game and I'm not really sure if I'm moving in the right direction. I want my code to be API and platform agnostic as I can. I'm currently developing on Linux using OpenGL and plan to develop for Windows using OpenGL as well, though I want to allow the potential for other APIs like DirectX. My starting point is window and context creation.

Share this post

Link to post

Share on other sites

Second, unless you are sure you had enough experience, such like developed several complicated games on both Windows and Linux, I would not suggest to write your framework or library directly.
Instead if I were you I would develop a real game, and then extract the independent code from the game to a library, then wrap to cross platform.

Developing a framework without real application, you will found your framewok can't be used in real app after you finish it.

Share this post

Link to post

Share on other sites

At this point there is nothing that strikes me as the wrong direction particularly, but you do need a stronger focus on organization.

If you want to support many platforms, you need an organization more like what I explained here.
Don’t clutter your code with a bunch of #ifdef’s.

You also need comments.
Every function should be commented. I use Doxygen style because my work needs actual documentation. I recommend you treat your work just as seriously, but if you must be lazy you should still at least use some form of per-function commenting.

One thing I would not do is to select a graphics mode by name.
In fact you don’t need to support OpenGL and Direct3D within the same build. Games haven’t allowed dynamic switching between them for years. Make one build for OpenGL and one build for Direct3D 9. And one for Direct3D 11 later.
Even if you do have a mode selection routine, use enumerations instead of names. Names are easy to misspell and consume too much space. Enumerate things you want to support.

One of the things I mentioned here is never to declare and define a function at the same time (except for templates). Don’t put function definitions inside your classes. Put them at the bottom of your header file, or tucked away inside a .inc.

As for actual game organization, make one or two master functions for starting up the engine, and one for shutting it down.
I use two. First you initialize the memory manager, then create your game class and set its state factory and other things to prepare it for your game, then call a secondary initializer that tells the engine it is ready to create the window, start the sound device, etc.

This is an example (ignore my explicit use of lse::, I like to be very specific about what classes/objects/types I am referencing):

{ // Scope the game class so that it is destroyed before lse::CEngine::DestroyEngine() is called.
// We do not need to make a custom game class for this primitive demo. We would want to make a custom game class
// that inherits from lse::CGame if we wanted to handle any game data not handled by lse::CGame. Any real game
// will have a custom game class.
lse::CGame gGame;

gGame.SetStateFactory( LSENEW ::CStateFactory() );

// Before running the "game" we need to tell it where to begin (which state).
gGame.SetNextState( LST_S_MODELTEST, 0, true );

// From here, simply run the game. It will handle the ticking of the game object and passing window messages
// to it so it can handle input, etc.
iRet = lse::CEngine::Run();

}

// The game class and window have been destroyed by this point. Shut down the rest of the engine.
lse::CEngine::DestroyEngine();
return iRet;
}

The flow is simple.
The engine provides a base class called CState. When you make your game, your state classes inherit from that class, and you give the game class a state factory that can generate your custom states (by enumeration).
When you call Run(), the engine will execute the current state by calling its Tick() and Draw() functions as needed, which allows your custom game to handle its own logic and to perform its rendering.
When you want to move on to another part of the game, like going from the main menu to the options menu, to the gameplay screen, just call SetNextState() and the engine will go to that state on the next cycle.

You can set up your system how you like, but this is very modular and helps break the flow of your game down into manageable blocks.

I don’t see anything wrong with your current implementation as far as overall game construction goes. The hierarchy of your classes makes sense so far. Mainly you could just use some organization.

Regarding comments. To me, these pieces of code needs more comments then anything else, yet is has none. All the other functions you call in your example main are relatively self explanatory. I don't get it...

2

Share this post

Link to post

Share on other sites

That is true. I should comment those initializers more than I did.
I have Doxygen documentation which explains what those calls do, and I guess I figured that was enough, but in actuality there is not such a thing as “enough” commenting (within limits of sanity).

The first one just sets up the memory manager, telling it how big to make the initial heap and the “true” indicates that the heap is growable.
The second one specifies the game class, windowed resolution, full-screen resolution (0 = desktop resolution), window title, and whether or not it is full-screen or windowed. False = windowed.

L. Spiro

0

Share this post

Link to post

Share on other sites

I have a question myself, YogurtEmperor. What is the purpose of this:
[source lang="cpp"]
static_cast<HINSTANCE>(_hInstance);
static_cast<HINSTANCE>(_hPrevInstance);
static_cast<LPWSTR>(_lpCmdLine);
static_cast<INT>(_iCmdShow);
[/source]It looks like you're casting the parameters to the types to which they're already defined in the parameter list, and then not really doing anything with them...?

0

Share this post

Link to post

Share on other sites

In Microsoft Visual Studio I compile with warning level 4 and fix all warnings so that my products never produce errors or warnings when built.
This is one way to get rid of the “unused parameter” warning.