Sign in to follow this  
metalmidget

After some general pointers on OO game design

Recommended Posts

I understand all the basic principles and theory behind OO, and I do have some experience writing classes with inheritance, polymorphism etc, but I was after some general tips about the usual way of designing OO games. The game I'm going to be creating will be a clone of The Incredible Machine, which basically involves putting all sorts of gadgets and components on the screen to create a big Rube Goldberg device to achieve some goal. At the moment my plan was to have a (probably abstract) base class (called entity or component or piece or something like that), from which I'll then derive all the different gadgets my game will have. They'll all be stored in a vector of pointers to the base class, allowing me to then just iterate through the update() and draw() methods of each element in the one vector for every frame. Is that a decent way of doing things? Are there any other important things I need to ensure I do/don't do? cheers, metal

Share this post


Link to post
Share on other sites
Sounds ok; as long as there aren't any components that need to update but are invisible, or need to draw but not update.


More advanced game frame-works would often divide components further into their physical components (for physics/collision detection), drawable components (for rendering) and logical controllers / entities (for updating).

For example - With your design, in each component's Update function, it would have to do it's own collision tests.
If you made physical objects a separate class hierarchy, and had entity objects contain pointers to physical objects then you could have a PhysicsManager class do all the collision tests itself. Then in the entity object's update function, it could query it's corresponding physical object to whether it has been bumped around or not, and respond accordingly (such as exploding, or starting a motor, etc...).

Furthermore, you would no longer need each components to know how to draw itself. Instead, each entity would register it's own drawable privative types (bitmaps/polygons/etc) with some kind of RenderManager.

Share this post


Link to post
Share on other sites
Hmmm... Sounds good in theory, and I kinda get what you're saying, but I can't really see myself implementing something that complex at this stage, without making a huge mess of it. How can I learn this sort of complex design? I'm at uni at the moment to engineering and computer science, but we don't get into OO until 3rd year I think (out of 5 years), so I'm self-taught in everything programming. I have a couple of books, but they focus mainly on 3D graphics. The 3D stuff has game code as well, but the 2D section is sort of a crash course in D3DXSprite before moving on to 3D.

cheers,
metal

Share this post


Link to post
Share on other sites
The classes I took at the university on OO programming did not prepare me for working on large game projects. I feel its really one of those things where practice makes perfect. The best way to learn (imo) is to make a game, and make the whole game. Finish what you start. Once you have finished one project, then start another one and learn from your mistakes.

For me, OO design is like legos. You have your basic objects which you use to build more complex objects. The tricky part is determining what types of basic objects you need for your game. If I was you, I would spend some time trying to flesh out the game you want to make. Even go as far to create a design document (if you haven't already). Once you have a list of things you want to do, then its much easier to determine what types of base objects you need. It looks like you have already identified two basic object types for your game: something that can be drawn, and something that can be updated. Are you going to assume that everything in the game that can be drawn can also be updated? Or do you want to allow objects to exist that can be drawn but not updated, and vice versa? Of course you could just make an object with both functions and have them do nothing, but then you have objects with draw functions that actually can't be drawn and that goes against the spirit of OO design. These sorts of decisions probably won't make much of a difference on smaller projects (say less than 10,000 lines) but as programs get larger and it becomes harder and harder for you to visualize how every system of your game works, how you chose to design your basic objects will really start to play a role. The main thing is to choose the right tool for the job. Don't build a rocket when a BB gun would suffice.

Kind of a vague answer, but you did ask kind of a vague question. :P

Share this post


Link to post
Share on other sites
That will work, but if you're really trying to stick to OOP best practices, you'll need to change it up a bit.

Be careful about how your base Entity class is designed. You don't want your object heirarchies to get too deep and complicated. Often when a programmer first learns of inheritance, they insist on making everything inherit from everything else. Make sure to differentiate between "My class IS a ..." versus "My class HAS a ..." A prime example is: "This class IS a ball, and a ball HAS a position." And because a position can be had, position is probably an object in its own right.

Also it is not a good idea to put your drawing code inside your game objects. A game object should not know how it's drawn. In fact, it shouldn't even care whether it's being drawn or not. A separate class should be responsible for drawing your objects. You do not want to be required to modify every Entity class whenever you make a change to your drawing system. Maintaining this will get out of control fast. Not only this, but you could implement different renderers for different graphics APIs by reimplementing only your rendering classes without touching your entity classes.

Share this post


Link to post
Share on other sites
I tend to consider "let's have all game objects inherit from a base class" an exercise in futility. What are the advantages of such an approach? If you answered "all game objects can then be used the same way", then you've made a classic mistake. You are right that doing so will indeed allow all game objects to be treated equally—but this is no advantage.

See, a game object is a complex beast. Some objects can be drawn while others are invisible. Some objects will move while others won't. Some will need regular updates while others never will. Many will have specific properties that others do not have, such as movement or collision or player control or whatever. Would you treat a chess board in the same way that you would treat a chess piece? There are, inevitably, different categories of game objects which will need to be treated differently. And making all objects identical by reducing them to a base class will only make that treatment harder.

Many see (and sometimes teach) object-oriented design as the process of designing solution-space objects to represent problem-space concepts. As such, every object-oriented designer should start solving a problem by blueprinting objects, complete with public interfaces and inheritance trees, and the resulting set of objects will magically happen to solve the original problem. Except it won't, and if the designer is lucky enough to avoid analysis paralysis of "everything is an object", he will still risk designing many useless classes which seemed a good idea at the time but serve no real purpose in the end program.

This is because using object-oriented programming to solve a problem entirely misses the point of object-oriented design. Writing a program generally consists in a repeated application of the three-step "Make it Work, Make it Right, Make it Fast", which could be also paraphrased as "solve, refactor, optimize". Object-oriented design fits within the second step of this process: refactoring. In short, its purpose is to make an existing solution easy to extend, reuse and manage—so, using object-oriented design to either solve a problem, or to optimize a program, generally results in lots of pain and tears.

Some designers can solve a problem and refactor the solution on the fly, and there are know techniques to implement a solution directly as an object-oriented design, but all of this requires, first and foremost, that a solution exists. Once you know how your program will do the things it's expected to do (or at least the features from version zero), implement the solution and refactor it into an object-oriented design. If your refactoring is correct, then the resulting code will be easy to extend for adding new features.

Share this post


Link to post
Share on other sites
Quote:
Original post by metalmidget
How can I learn this sort of complex design?

Strangely enough, when I was at Uni the subject that seemed to improve my OO the most (even though it wasn't to do with OO) was Database modelling!
Database normalization is a much older field of study than OO, so my lecturers were probably better at teaching it. Anyway, when designing a relational database, if you follow the principles set down by these normalization gurus, it's actually pretty simple to check if your design is "correct" or not (that is, whether your design needs to be split further into smaller parts).

Even if you don't care about databases, I'd recomend reading about at least the first three normal forms and see if it makes anything click for you.

[edit] Also, Scott Meyers' books on C++ helped out the clarity of my OO designs greatly.

Share this post


Link to post
Share on other sites
I have a suspicion that the whole OO analyis and design thing is presently in a really confused state and that there is something of a lack of real critical analysis going on.

Take for instance oo design patterns - these are taken to represent a level of programming abstraction that is one level above the syntactial object orientated facilities that are provided for in any given oo language. the authors of the GoF book, design patterns famously state, “prefer composition over inheritance”. however it never seems to be noted ( at least its difficult to simply reconcile) that this flies in the face of 2 of the 4 most commonly cited attributes of OO design - namely polymorphism, and inheritance ( along with encapsulation and modularity).

Maybe the gof book is merely an extremely well disguised and covert attack on OO theory - i dont know. i guess i feel that with basic contradictions like this, it is really hard to make generalisations about what might be considered good oo design or how to go about learning what would be called 'best practice'. for what its worth, I agree that polymorphism and inheritance (eg factoring base behavours into objects and then deriving) is a really tight coupling and that the gof authors when criticising this approach are probably speaking from real problem solving experience.

The design patterns books and the threads on compenent based development here on gamedev always challenge my understanding and help me to think about problems in different ways. also i can totally relate to the above post - right now i am involved in a db project which involves a lot of table/data normalisation and i catch myself wishing that i had a better grasp on relational theory - since there do appear to be interesting implications for general programming.

Share this post


Link to post
Share on other sites
Wow, thanks a lot guys. OK I'm trying to take all this on board but at the moment the one thing that has struck me as something that I could probably 'fix' in my design would be to not have objects know how to draw themselves. So how would I implement that? I get the concept of having a seperate class as a render manager, but how would it work? Would it have its own list of all objects that need drawing every frame and how to draw them? Would it have separate drawing functions for each type of object and then have each of those functions called from the game loop, passing a list of that type of objects as the parameter?
What I did in my last project (Tetris, now finished) was pass a pointer to a D3DXSprite object to the draw function of each existing block.

Also, in reading some of you guys' posts, I was thinking "yes, everything will need to be updated eveyr frame, because I'll need to check collision." Having thought about it though, would it be a better idea to only update stuff that needs moving or other processing done on it? And then do collision detection via a separate class? That would remove the need to call update functions for, say, a brick or platform.

This is all a lot of ideas and jumbles of words at the moment for me. At some stage I guess I'll sit down, do the unthinkable, and actually design my game- class names, inheritance trees, what functions will do what etc. People always say to do that but I can never be bothered. For something slightly more complicated it's probably a good idea though. Otherwise I can see myself having to completely redesign everything halfway through because my 'make it up as I go along' approach wasn't working... :S

Thanks again guys,
metal

Share this post


Link to post
Share on other sites
Quote:
Original post by metalmidget
Wow, thanks a lot guys. OK I'm trying to take all this on board but at the moment the one thing that has struck me as something that I could probably 'fix' in my design would be to not have objects know how to draw themselves. So how would I implement that? I get the concept of having a seperate class as a render manager, but how would it work? Would it have its own list of all objects that need drawing every frame and how to draw them? Would it have separate drawing functions for each type of object and then have each of those functions called from the game loop, passing a list of that type of objects as the parameter?
What I did in my last project (Tetris, now finished) was pass a pointer to a D3DXSprite object to the draw function of each existing block.

Also, in reading some of you guys' posts, I was thinking "yes, everything will need to be updated eveyr frame, because I'll need to check collision." Having thought about it though, would it be a better idea to only update stuff that needs moving or other processing done on it? And then do collision detection via a separate class? That would remove the need to call update functions for, say, a brick or platform.

I'm also interested in the answer to these questions... :)

Share this post


Link to post
Share on other sites
Quote:
Original post by chairthrower
The authors of the GoF book, design patterns famously state, “prefer composition over inheritance”. however it never seems to be noted ( at least its difficult to simply reconcile) that this flies in the face of 2 of the 4 most commonly cited attributes of OO design - namely polymorphism, and inheritance ( along with encapsulation and modularity).

AFAIK, the GoF make that statement to discourage the over-use of inheritance when it is not actually required (hence the use of the soft wording "prefer").

Inheritance is used to model the "is a" relationship between two entities (and to enable polymorphism).
If you don't explicitly need an "is a" relationship, then you also don't need polymorphism *in that particular case*, which means that inheritance is the wrong tool *in that particular case*.

For example, lets say we're writing a queue class, which we want to implement by reusing the existing std::vector class. We can do this either by composition or by inheritance.
Both of these approaches work, so they are both "right". However, one uses a model where the "queue is a vector", which is obviously incorrect ("bad") as the queue does not contain all of the functionality which a vector has.

//Using inheritance: "bad and right"
template<class T>
class MyQueue : private std::vector<T>
{
public:
void push( const T& in )
{
push_back(o);
}
bool pop( T& out )
{
if( empty() )
return false;
out = front();
pop_front();
return true;
}
};

//Using composition: "good and right"
template<class T>
class MyQueue
{
public:
void push( const T& in )
{
data.push_back(o);
}
bool pop( T& out )
{
if( data.empty() )
return false;
out = data.front();
data.pop_front();
return true;
}
private:
std::vector<T> data;
};





Quote:
Maybe the gof book is merely an extremely well disguised and covert attack on OO theory - i dont know. i guess i feel that with basic contradictions like this, it is really hard to make generalisations about what might be considered good oo design or how to go about learning what would be called 'best practice'.

It's not a contradiction, just generalised advice. It's only contradictory if you misinterpret it to mean that you shouldn't use inheritance ever (instead of meaning that you should only use inheritance when it is actually useful).

Share this post


Link to post
Share on other sites
Quote:
Original post by chairthrower
I have a suspicion that the whole OO analyis and design thing is presently in a really confused state and that there is something of a lack of real critical analysis going on.


That and it's a relatively novel arena within an already relatively novel field.

Quote:

the authors of the GoF book, design patterns famously state, “prefer composition over inheritance”. however it never seems to be noted ( at least its difficult to simply reconcile) that this flies in the face of 2 of the 4 most commonly cited attributes of OO design - namely polymorphism, and inheritance ( along with encapsulation and modularity).


Except the 5 prime elements of object oriented programming (Single Responsibility, Liskov Substitution, Open-Closed principle, Interface Segregation, Dependency Inversion) don't explicitly rely upon inheritance.

Indeed, deep inheritance trees will naturally tend towards violating Interface Segregation (as the base interface will grow to encompass anything the inheritors might do), or the Liskov Substitution principle (you do type-checks on base classes to see if this is a specific derivation).

Quote:

for what its worth, I agree that polymorphism and inheritance (eg factoring base behavours into objects and then deriving) is a really tight coupling...


See Dependency Inversion link. Variable behaviors are best abstracted into a strategy or similar pattern. In general though, if you find things tightly coupled then you're not abstracting enough.

If you have two things that know about each other, but shouldn't then you need a 3rd object that knows of both so that they may freely be ignorant of each other.

Quote:

The design patterns books and the threads on compenent based development here on gamedev always challenge my understanding and help me to think about problems in different ways. also i can totally relate to the above post - right now i am involved in a db project which involves a lot of table/data normalisation and i catch myself wishing that i had a better grasp on relational theory - since there do appear to be interesting implications for general programming.


Indeed. I found some of the database normalization tidbits wonderfully applicable to OO design. It's key though to keep them a bit separate for the most part. Data normalization is for data. OOP is (or at least should be) more than just bundles of data.

Share this post


Link to post
Share on other sites
Woah, I can see this thread rapidly spiralling off-topic into a general discussion about inheritance and OO design. I realise my original question was quite vague and wide-ranging, but I'm after some advice, specifically, on a decent structure of what should do what for my game. I sort of made a start on a render manager class. Am I heading in the right direction?


/* i've only included the members that show the general design of my rendering
system*/

//CBall.h:
class CBall
{
public:
CBall(float X, float Y, float rotation, BALLTYPES type, CRenderManager* renderer);
/*constructs object and adds it to the render manager's list of balls
to draw*/

~CBall(); //destroys and removes from list of balls to draw

friend CRenderManager; /*so the render manager can access its members to
know where to draw it*/


protected:
//there are more data members, these are the ones related to rendering
D3DXVECTOR2 mPosition;
float mRotation;

D3DXVECTOR3 mCenter;
RECT mSourceRect;
CRenderManager* mRenderer; /*pointer to the renderer. this is needed so
that the ball can (in its destructor) remove itself from the list of objects to
be drawn*/

int mRenderID; /*where it sits in the render manager's
vector of balls*/

};

//CRenderManager.h:
class CRenderManager
{
public:
CRenderManager(); /*creates a D3DXSprite interface and loads all
textures*/

int AddBallPointer(CBall* ball); /*adds a ball to the list of things to
draw*/

void DeleteBallPointer(int element); //the opposite
void Render(); //duh

private:
//spriting interface
ID3DXSprite* mSprite;

//texture objects
IDirect3DTexture9* mTexBalls;
//more to come when I have more stuff to draw

//lists of objects to draw
vector &lt;CBall*&gt; mpBalls; /*note that this is a list of pointers to
CBalls. So the balls themselves aren't members of the render manager. The balls
and manager exist on the same scope level, hence the manager being a friend of
CBall.*/

};

//CRenderManager.cpp
int CRenderManager::AddBallPointer(CBall* pball)
{
/*CBall calls this from its constructor, using 'this' as the argument.
So a pointer to the ball is added to the list, and the ball is then told its
index within the vector as the return value, so it can delete itself later on*/

mpBalls.push_back(pball);
return mpBalls.size() - 1;
}

void CRenderManager::DeleteBallPointer(int element)
{
/*Cball calls this from its destructor, passing the number obtained from
AddBallPointer as the parameter. only the pointer to the ball is deleted, not
tha ball itself. */

mpBalls.erase(mpBalls.begin() + element);
}

/*CRenderManager::Render is fairly self explanatory. It just iterates over the
vector, mpBalls, obtaining each ball's properties to use as arguments to D3DX
functions.*/



Things will become more generalised than this once I have more objects. Ie- I won't have AddXPointer() and DeleteXPointer() for every type of game object. This is just a prototype.

cheers,
metal

[Edited by - metalmidget on May 13, 2008 5:43:48 AM]

Share this post


Link to post
Share on other sites
Perhaps something like the following may help;


class Game: // does initialisation of other classes, and holds game loop code
HAS:
class Input // deals with all the input devices you need for the project (keyboard, etc.)
class Render // deals with setting up the screen, and the rendering
class Physics // three guesses :)
class Scenegraph // holds the data for the scene
class Resource // all resources are given keys to refer to them by

the class that everything revolves around here would be the scenegraph, where each node could be given a pointer to a physics "body" and render "model", which would be classes used by (but not inherited from) the Physics and Render classes.

Notice I said that the nodes have pointers to the body and model. This keeps the different parts of the game separate, but allow you to deal with each game object (whether it is a car in a racing game, or an monster in an RPG) in a single location.

Share this post


Link to post
Share on other sites
Quote:
Original post by webwraith
Perhaps something like the following may help;


class Game: // does initialisation of other classes, and holds game loop code
HAS:
class Input // deals with all the input devices you need for the project (keyboard, etc.)
class Render // deals with setting up the screen, and the rendering
class Physics // three guesses :)
class Scenegraph // holds the data for the scene
class Resource // all resources are given keys to refer to them by

the class that everything revolves around here would be the scenegraph, where each node could be given a pointer to a physics "body" and render "model", which would be classes used by (but not inherited from) the Physics and Render classes.

Notice I said that the nodes have pointers to the body and model. This keeps the different parts of the game separate, but allow you to deal with each game object (whether it is a car in a racing game, or an monster in an RPG) in a single location.

(I'm assuming that a node is some sort of thing in my game)
So would that mean that for every physical object (node) in my game (as in, each ball, spring, etc), there will be 3 objects in code? The first in the scenegraph, which has pointers to the other 2 which are in the physics and render classes?
Won't there be some overlap of data there? For instance, the objects' size and position are important to Physics for collision, and Render for graphics. Do they both maintan their own set of data for the object? Or is all the data for the object stored in classes contained within SceneGraph, and that data is merely checked and operated on by Physics, and used by Render?


Oh and what do you mean by a resource? Sounds, textures, etc? What about level data loaded from a file- is that a resource?

How would this structure work across the life of the app? Would it be something like:
Game would call functions of Scenegraph to create game objects. The constructors of these objects would then call functions in Render and Physics which create the corresponding objects in those classes.
<Something else, see questions later>
When the objects are no longer needed, a function in Scenegraph is called to destroy the objects (called perhaps from Game or Physics). The destructor of these objects would then call destroy functions in Render and Physics to delete the corresponding objects.
I'm not sure what goes in the middle though, in terms of the game loop. Does the game loop call a function in Scenegraph which somehow calls the update() and draw() functions in Physics and Render? Does the game loop call all 3 of those functions?

Sorry- I know I'm asking a ton of questions at once, but I'm determined to get this right!

cheers,
metal

Share this post


Link to post
Share on other sites
A resource is anything you would load from a file, or anything that isn't hard-coded data.

Nodes are specific classes that the scenegraph uses to store relationships between your game objects.

Lets say you're creating a small, 2D ship game. The game is played on a single screen.

Each ship could move about on the board anywhere there wasn't another ship.

Your resources would be something like;
@ ship image - this holds all the images of the ship in the different directions.
@ background image - this is an image of the sea
@ ship info - if you have different ships with different abilities, you'd need something like this for each one
@ config info - holds info like the screen resolution and colour depth, etc.

You load these in an Init()-style function in the Game class. Items like the config file may also be required by the Render class, so initialise your other classes here as well.

During your game loop, your player creates a new ship. You add a new "ship" node to the scenegraph, with its parent set to the root of the graph (there's no need to do otherwise just yet, I'll give some examples later),create a new renderable object with the previously loaded image and tell the new node about it, create a new physics "body" and tell the new node about it, and set the nodes starting position to whatever. The Physics class needs to know about the node, as does the Render class.

During the Update() function, input data is retrieved, the Physics class updates, both of which could change the position data in the node. Note that the node itself hides the Render class from having to know about the Physics class.

Then, during the Draw() function, the Render class has a list of objects to draw, it can get their specific positions from the Nodes they belong to, and can be stored in a render-effective way (by lumping everything that needs a certain shader together, so they all get drawn at the same time, and so on).

Then, once the game is over, you would just call each of the classes Cleanup() functions, which would destroy their lists of objects.


Doing things like this gives all the different parts of the program a universal way to communicate(the nodes in the scenegraph class for actual game objects, and the resources for the data that stays the same between games), and they can store their own information in a decent way for whatever job they need to do.

Examples for giving nodes parents, would be (and remember, this is just an example!) having a glowing symbol that rotates around your characters head to show their status in an RPG.

At least that's just something I came up with, but it sounds like you have a fair idea of what I meant anyway.

As you can see, updating the parts in my example is done by calling an Update() command in the Physics class, and reading in input.

Technically there would be three objects in code, as you would need to store the info for the ship (or ball as per your example) in the correct system (speed and mass in Physics, the image or model in Render), and the node would bring the different data together.

Share this post


Link to post
Share on other sites
...
Brilliant.
That's exactly the kind of stuff I needed to know. I'll need to read it over a few times to let it sink in properly, but I think I get the basic gist of it.
I'm just a little unsure of exactly what you mean by a node? How is it different to a game object (eg ship, ball, bullet etc), and what do you mean by its parent?

Thanks a lot,
metal

EDIT: If you're still online and reply straight away, I probably won't read it until morning. I'm aware it's only early afternoon in the UK, so it'll probably be mid-late evening for you until I get a chance to have a look.

Share this post


Link to post
Share on other sites
basically, in the examples I've given a "node" is just what you called an "entity" in your original post. The only possible difference between the two was that in your case, you were probably thinking about keeping all the data for each game object together, while I split it up, and provided an object that kept a reference to the data so it didn't have to be lumped around all over the place.Besides that, I just call your entity a node, due to the shape of the scenegraph being a tree container. Well, in my case, it's technically a map, but anyway... :P

Share this post


Link to post
Share on other sites
Quote:
Original post by chairthrower
The design patterns books and the threads on compenent based development here on gamedev always challenge my understanding and help me to think about problems in different ways...


Are there any links on other component based development threads?

I love these kind of mind-enhancing discussions.

Share this post


Link to post
Share on other sites
Quote:
Are there any links on other component based development threads?


If you search on combinations of 'entity', 'component' 'subsystem' there are quite a few threads. I found these two threads to have a lot of great ideas.

Sneftel's Outboard component-based entity system architecture - although i think i might have read somewhere else that he has since refined the approach presented here

and this post, Entity System question by Aeroz and the following discussion

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this