After some general pointers on OO game design

Started by
18 comments, last by chairthrower 15 years, 11 months ago
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
Advertisement
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.
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
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
scottrick49
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.
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.
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.
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.

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
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... :)

This topic is closed to new replies.

Advertisement