Module Development for Game Design - By Jeff Verkoeyen [Archive] - C Board

PDA

View Full Version : Module Development for Game Design - By Jeff Verkoeyen


kermi3
02-08-2005, 11:43 AM
Article Number Two:

http://www.cprogramming.com/tutorial/gamedesign.html

Sang-drax
02-08-2005, 11:59 AM
Objects in a game are also very good to represent with abstract base classes.


class Object
{
public:

Object();

virtual void process() = 0;

bool isAlive();

int getPriority();
private:
int priority;
bool alive();
};

//Other abstract subclasses
class GraphicObject;
class ConcreteObject;


Then the game loop can process every object in the game using a container:


std::list<Object*> objectList= ...

while (true)
{
sort(objectList); //Sort by priority
for each (object <- objectList)
{
obj->process();
if (!obj->isAlive)
objectList.remove(obj);
}
}

New types of enemies, weapons etc. can be added to the game without recompiling the game loop or almost anything else.

skorman00
02-09-2005, 12:03 AM
The modular Game class is a great method to use in any project, especially a party game consisting of several mini games.

As with game entities as Sang-drax indicated, it is sometimes useful to create an abstraction layer on aspects of game logic, rendering, or any other component using similar methods. This is most beneficial when their is a high risk of problems with the chosen technology being used.

These are common practices in other software industries, but, unfortunately, still foriegn to several game developers.

Hunter2
02-09-2005, 11:36 AM
That's a neat and efficient system to use. However, how could this be applied to making menus (an important module to include in any game)? What if you wish for your game to remain running in the background while you access a menu, i.e. you don't want it to shut down every time you go to the menu screen? The system works fine if you simply want the game to change states, but it seems to me that having overlapping states might require a more complex mechanism - perhaps a module loop discretely nested in the main game module's Execute() function. Even then though, it would probably pause/stall the game while accessing the menu. Does anyone have better suggestions? (Or is multithreading always the answer to every question in life?)

skorman00
02-09-2005, 05:56 PM
Module* oldMod=gamemod;
if((gamemod=gamemod->ExecuteModule())!=oldMod)
{
lastMod=oldMod;
lastMod->Shutdown();
gamemod->Initialize();
}


He pointed out that this bit of code can be modified to fit needs. Directly before, or after this check, you can add a loop to control the menus, and exchange information to the game module through several methods. Personally, I think a singleton model would work well in such a situation.

Bubba
02-10-2005, 12:20 AM
Ahhhhhhhhhhhhhhh!!!

Virtuals. I avoid them like the plague in game programming. Don't use virtual functions.

CornedBee
02-10-2005, 02:27 AM
You can overdo anything, and avoiding virtuals in one of these things. What alternative approach do you suggest? Virtual calls are faster than switches, unless they're implemented as jump maps - which is far less extensible.

Hunter2
02-10-2005, 10:10 AM
>>Virtuals. I avoid them like the plague in game programming. Don't use virtual functions.
May I ask why? It's not like loading/unloading modules is a time-critical operation running in a tight loop anyway. The time spent in actual initialization and destruction will almost certainly overshadow virtual call overhead by such a degree that it becomes neglible.

Bubba
02-11-2005, 05:31 AM
Virtuals can result in a lot of function call overhead and often times they are misused to the point that this side effect begins to affect the performance of the game. It's probably not critical in applications but it is inside of a main game loop.

I was going to implement a renderer using virtual functions because it seemed the place to do so. However after reading several books and articles on the topic I decided against it. Instead the rendering is done inside of one main render function. The render function used to call the virtual render function of the class. The problem is that this resulted in a lot of function call overhead inside of a very time critical loop which was quite noticeable. Not only did I have to remove the virtuals but I also had to be careful about how/if I copied structures when rendering objects. What I eventually came up with was this:

Since Direct3D is essentially a state machine there are only so many 'useful' render states. These can be preserved using mechanisms provided in Direct3D called state blocks or you can implement them on your own.
The renderer should draw all objects from one vertex buffer
The renderer should call SetTexture() only as many times as is needed and no more.
The renderer should call SetTransform() only as many times as is needed and no more.
The renderer should NOT call out to any other code to render objects.
The renderer should render similar objects in batches.
The renderer should NOT check the current state block.
The renderer should NOT have to check any conditionals in order to properly render batches
Switching batches and states should be a simple matter of incrementing an integer which acts as an index


What this amounts to (so far) is this:

Prior to rendering

Cull objects based on frustrum and add vertices to master list if object passes test - add vertices depending on z order and/or priority of object (alpha blended objects must be rendered in correct order or they wont look right)
Add object state information to master object state vector
Add object transform to world transformation stack
Set the vertex buffer up so that vertices that use the same texture are sequentially arranged inside of the buffer
...
...


Rendering

Retrieve and setup block state from vector of states to be rendered
Retrieve index range information for this batch of primitives from object state vector
Retrieve primitive type for this batch from object state vector
Retrieve texture for this batch from object state vector and call SetTexture()
Retrieve world/view transform from matrix stack
(Optional)Retrieve vertex shader ptr from effect file for this batch from object state vector
(Optional)Retrieve reflection flags (if any) from object state vector
Set all shaders/states based on object state and shader info
Render the batch
Increment batch number counter
Repeat process until all batches have been rendered


What I'm trying to do is sequentially render everything. This amounts to simply incrementing a vector or an integer that indexes into mutliple vectors. As long as everything is lined up correctly in the vectors it should work. Same principle as when rendering bitmaps. You want to linearily traverse the array instead of doing a multiply to find every pixel in the bitmap.

Fact is this. If you call a virtual render function each time and that render function sets up all the states for an object, then if you have 600 objects on screen you will be rendering each object one at a time and setting the state 600 times, setting the texture 600 times (even if some share the same texture), setting the world/view 600 times, etc, etc.

So perhaps this isn't just a matter of not using virtuals..it's a matter of once you enter the main render loop...don't leave it until you are done with the scene. It's not easy to set it up this way but Direct3D works a lot better with one huge batch of primitives than with several hundred small batches of primitives. Virtuals already lend themselves to some call overhead and as you see I'm trying to eliminate almost all of the calling overhead. Calls to SetTransform() and SetTexture() cannot be avoided but a good engine will only call them when absolutely necessary. I left some of the list blank because there are certain things I'm still trying to hammer out with this structure.

That's the method behind my madness and my reason for not using virtuals. And no, I haven't figured out how to get all of this to click yet but I will. I'm having a lot of problems with the world/view transformation - no object will occupy the same space at the same time. So you must always change the translation. But if you use a matrix stack you can effectively do the multiply for the object, pop the stack, and get back to the original matrix. This prevents you from having to compute the inverse to get back to the original matrix.

Stack on entry to Render:

..Base World/View Matrix ( World is identity)


Stack on first batch:

..Transformation/scaling matrix for batch of objects
..Base World/View matrix

So you see that even if you have a complex model with elbows and joints you only have to track how many times you pushed a new matrix onto the stack. Pop off that many and you are automatically back at the original matrix which would transform an object to 0,0,0 in world space. There are other ways to do this as well.

Sang-drax
02-11-2005, 09:20 AM
Well, it's not exatly the virtual function call itself which kills the performance then. It's more drawing 600 objects at once versus drawing them one at a time.

And if you use virtual functions like Jeff Verkoeyen suggested in the article, you won't be able to notice any performance difference.

Bubba
02-12-2005, 12:54 AM
It's a matter of executing function calls inside of a render loop.

Hunter2
02-12-2005, 10:58 AM
If I'm not mistaken, it isn't inside a render loop - it's a mechanism by which you can switch between game modes (i.e. main menu, playing game, high scores, whatever). The render loop would be placed inside the overloaded virtual Execute() function of one module, not vice versa.

CornedBee
02-12-2005, 11:23 AM
BTW, bubba, each call into DirectX is a virtual function call...

jverkoey
02-12-2005, 11:25 AM
It's completely up to the coder where/how to implement that code. I use it inside of a render function that only gets called once per frame. I personally have not noticed any large performance hit on any of my games by using this system and have found that the amount of code readability/reusability is increased tremendously.

However, if you are truly worried about speed, you could just have it so each module has its own render loop and returns from that loop when it's finished. I personally would find this to be more of a hassle than anything....but whatever floats your boat.

Bubba
02-12-2005, 03:02 PM
BTW, bubba, each call into DirectX is a virtual function call...

I was not under the impression that COM was using virtual function calls since COM is a binary standard and not true C++.
Even though everything is derived from IUnknown it is not the same as a C++ class.
Please expand on this.

CornedBee
02-12-2005, 05:10 PM
I was not under the impression that COM was using virtual function calls since COM is a binary standard and not true C++.
Even though everything is derived from IUnknown it is not the same as a C++ class.
Please expand on this.

COM is a binary standard indeed, but once that coincides with the way the MS C++ compiler lays out a C++ class that only has pure virtual functions.

So, to define an interface, you simply do this:
struct IUnknown
{
virtual UINT __stdcall AddRef() = 0;
virtual HRESULT __stdcall QueryInterface(void **ppv, REFIID iid) = 0;
virtual UINT __stdcall Release() = 0;
};
Without treating it specially in any way, the C++ compiler will emit code that is binary compatible with COM.

A COM object is just a class that inherits any number of interfaces and implements all the functions inside.

So yes, COM is just a binary standard, but it says about this:
An interface pointer points to a memory location where the first thing is a vtable pointer. This vtable pointer points to a memory location that contains function pointers. To call a function through COM, you first push the interface pointer to the stack, followed by all the parameters, from left to right. Then you dereference the interface pointer. You add the function-specific offset to the resulting pointer and call to the resulting address. After the call returns, you don't need to clean up the stack.

This is the exact way the MS compiler implements virtual function calls that use the __stdcall convention. The difference to the standard C convention is in the way parameters are passed, but not the call mechanism.

The bottom line is that the COM call overhead is equal to a virtual function call.

Bubba
02-13-2005, 12:50 AM
Thanks a million. I've not designed my own COM objects yet but that info is extremely useful.

The point I'm making about games here is probably only relevant to those that require a lot of processing power for the graphics and game content. I've noticed extreme performance degradation as a result of copying structures during the main render loop, calling my own virtual functions within a render loop, and of course accessing anything on disk during the main render loop.

So to circumvent the problem I've decided that since the only difference between objects is usually:


Their orientation in 3 space - translation, rotation, scaling, etc. - essentially their world transformation matrix
Their texture(s) - these should be maximized so as to wrap around the object - thus using 1 texture for a mesh.
Their primitive type info (some objects have multiple primitive types)
Their texture blend states and texture addressing modes
Their vertex type and/or FVF


Because most objects only differ by these properties (there are probably more you can think of) it makes sense that one render function could be used to render the entire scene.
This approach is much different than having a virtual render for objects or having a render function for each object. This method results in virtually no outside calls being made which results in faster renders.

Look at your rendering code and you will see that most of them in Direct3D will look something like this:


void Object::Render(float fFrameTimeDelta)
{

D3DXMATRIX World,Translation,Rotation,Scaling;
D3DXMatrixRotationYawPitchRoll(&Rotation,Object.Rot.x,Object.Rot.y,Object.Rot.z);
D3DXMatrixScaling(&Scaling,Object.Scale.x,Object.Scale.y,Object.Scale .z);

World=Scaling*Rotation;
World._41=Object.Pos.x;
World._42=Object.Pos.y;
World._43=Object.Pos.z;

Device->SetTransform(D3DTS_WORLD,&World);

//Setup render/texture stage states for this object
//...

//Set texture

//Draw Object
}


You can clearly see that nearly every object will follow this except for special effects which might also need to use the view matrix to invert it, etc. But for the most part objects all follow the same rendering path. Because of this it seems to me that one could place the information inside of what I would call object state blocks that would tell the renderer all the pertinent information relating to the current object.

Also since most objects fall into similar categories (billboards, sprites, 3D objects, etc) it seems to me that they should be rendered in batches. As well since all objects must have a vertex buffer it seems plausible to use one huge vertex buffer and specify an index range in the object state block so the renderer knows which indexes in the master vertex buffer to use for this object. Primtive type could also be stored in this section as well or you could setup a structure that could handle multiple primitive types throughout a range of indexes.
Textures would simply be an ID within the class stating which texture the object uses. The actual IDirect3DTexture9 interface would reside in the global list for the currenttly loaded level in memory. This means that any texture can be used on any number of objects but yet still only reside in memory exactly one time. The same holds true for sound effects, etc. I can see a great benefit to storing the actual texture in a list instead of creating a new IDirect3DTexture9 object relative to the object class. You would also be assured of the same texture only existing once and only being destroyed once....you could effectively destroy the list of textures at any time you wanted to and be assured that no other instances of the interface were lurking around in your code. Nice and centralized.

I really think looking this stuff up based on indexes and using an incremental rendering approach with one huge vertex buffer would be much faster than calling virtual render functions for each type of object that has been derived from a base object. I could be wrong but I don't think I am.

Zach L.
02-23-2005, 04:18 PM
A quick note on the article itself:
Its presenting a technique that is useful not just for game programming (I won't debate its usefulness for that purpose, as I don't do any game programming, but it is certainly useful elsewhere), but I don't think it presents the idea extraordinarily well. Those who know the basic technique will see obvious uses, but without a few more concrete examples of how to use it (not necessarily code), I don't see a programmer without much OO experience gaining much from it.

Cheers

jverkoey
02-23-2005, 04:38 PM
*nods* That's understandable (I wasn't aware the article was being posted and therefor didn't get a chance to bulk it up a bit as I'd written it earlier under the impression I was under a size limit....)

However, here's a more detailed example (codewise), I'll add a graphical example later when I get home:

Let's say in a program you have multiple different parts. In this case, we'll use a game, just because it's easiest to describe and everyone's played a game before (I'd hope).

So, in this program, let's say we have a main menu, the game itself, credits, and an options screen.

One way of going about deciding which section you are currently running is by using a switch statement:


enum GameStates { MAINMENU, GAME, CREDITS, OPTIONS };
....
GameStates gameState;
.....

switch(gameState)
{
case MAINMENU:
runMenu();
break;
case GAME:
runGame();
break;
....etc
}


This is a very watered down version, most games will have many more game states than just this, and as you can see, for every state you want to add you have to add yet another test to the game loop that gets tested every frame.

The O notation for this sort of setup would be O(n) for every new module you want to add. This extra time is practically moot nowadays, but it also presents a problem with expandability and code reusability.

However, with a module-based design, you can have as many game states as your heart desires and the only hit you'll have is just a single virtual function call, which, for a large game, will be much more advantageous to an incredibly large switch statement.

So let's see a better example:

We have the game loop, as described in the article, and the base class for each module. Using this, we can create the game states very easily:


// GameModule.h
class GameModule : public Module
{
private:
public:
void Initialize();
Module* ExecuteModule();
void Shutdown();
};
extern Module* GameMod;



// GameModule.cpp
#include "GameModule.h"
Module* GameMod;

void GameModule::Initialize()
{
// Initialization code
}

Module* GameModule::ExecuteModule()
{
// Execution code
return GameMod;
}

void GameModule::Shutdown()
{
// Shutdown code
}


And then we need to initialize the GameMod pointer elsewhere at the startup of our program.

But basically, this is the fundamental design using the module system, I will upload my template code that I use when starting new projects when I get home, and hopefully that will make the whole thing a bit easier to comprehend.

jverkoey
02-23-2005, 11:21 PM
Ok, attached are the image and my template code I use in all of my games involving OpenGL. It is my template project and should be able to compile and create a program immediately. Assuming you want to actually use the code, however, you'll need a few files from my library, which are attached in a separate zip.

-note-
because of the stupidity of not being able to upload .zip files, you need to rename the two .txt files to .zip to open them correctly.

jverkoey
02-23-2005, 11:21 PM
Here's the pic

Bubba
03-03-2005, 04:07 AM
But this still does not lend credence to the virtual function argument. So far in my projects I haven't found a place where I absolutely need to use a virtual function, so I haven't. All of my modules are separate and to add functionality you simply create classes and add them into the existing CD3DApp class.

The CD3DApp class I've created sets everything up and rather than use a virtual render function and a virtual function call to figure out which texture to use, I use a global array. So texture switches amount to simply storing a texture ID for each texture and then setting the texture to that ID in the main loop. The more and more I code this stuff, the more I can see that there are about a billion ways to do it, but only a few that yield very good results. I'm still fighting for FPS in my games - the more optimization, the better. The fewer calls inside a render loop the better.

I fail to see the validity of the argument based on your examples.

jverkoey
03-06-2005, 02:38 AM
Yah, I can see what you mean, mainly from my own experiences with 2D games, I've found this system of development much easier than any other method I've ever used. The one drawback is that most of the time, 2D games aren't incredibly graphics intensive so it's hard to judge if one method is definitely faster than another one or not.

It basically comes down to a matter of stylistic choice and which method you prefer to implement in your own projects. For projects where you need to scrape every bit of efficiency out of your code, this system probably wouldn't be the best design. But for other smaller projects, it provides a simple means of setup and encourages code reusability.

Bubba
03-07-2005, 08:05 AM
Agreed. Good article and good discussion.