• Advertisement

Archived

This topic is now archived and is closed to further replies.

An elegant object-oriented way to design a game engine

This topic is 5897 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

EDITED TO ADD: I want to thank everyone for your insightful advices, this discussion really helped me. I'm about to start coding my first complete engine very soon! ---- Oww my head hurts so much! I have almost 50 scribbles, notes and ideas on how to structure a 2d game engine. I have a big problem : I keep thinking and thinking up until I find a flaw somewhere in my notes and know I can make it better. I then usually start over or put the "project" on hold. That's why I can never really finish a big project even though I got ton's of IMHO good ideas. I've tried looking at some open source code, but sadly most of theses engines are written in C, although I understand what exactly is C compared to C++, I find it awfully ugly and inelegant. Remember that my goal is educational. My goal : - For 2d games - Clean design - Clean code (no quick hacks) - Best OOP - Good comments (easy) I have some questions for the gurus: Should the blitting functions be in the sprite class ? or outside ? I'm leaning toward inside because I guess it would make things easier... or not.., but what about blitting the rest of the stuff like the background, that would mean duplicate function. :/ don't want that. Arghh my head What about sprite animations ? Let's say I have CAnim which include an array of pointers to CSprite objects. Good ? Good practice would be to separate Graphics from Collision Detection. But the bounding box (which would be used in Collision Detection) is stored in the Graphics (Sprite) what the fuck? Well, (right now) just thinking about it, I guess it would be ok since you would have a function for collision detection against all the entities which contain a pointer to a sprite which contain the bounding box. Argh, I got some more questions but this post is already long enough and 90% of it doesnt make sense to any of you. If some of you want to see my most recent plan, I'll digitize it and post it here for all to see and laugh at my pathetic attempt =) Edited by - hpox on November 24, 2001 10:55:02 PM

Share this post


Link to post
Share on other sites
Advertisement
Guest Anonymous Poster
I had this problem. The solution is easy: hack up an engine. Then do it again, but this time make it a little better. Do it a few more times. You need experience to make these kinds of choices. Every journey starts with a first step. It doesn''t matter if you trip and fall, just get up and get walking. Sure it won''t be pretty but your experiences will aid you on your next attempt. Just get something done. I like to have really pretty code, and I can''t stand C, but the simple truth is that you just have to start coding and make some mistakes. If things get too ugly start over. Don''t view it as a waste of time either.

Share this post


Link to post
Share on other sites

For what its worth this is exactly how I''m doing it. Its working for me (so far .

Code something (anything) first. Keep to good coding and OOP practice, but don''t worry too much about the elegance of the design for the first cut. You''ll invariably have to go back over it anyway.

One good tip I have picked up. Don''t spend too much time optimising as you go (bottom up optimisation). Get the structure right first.

Hope this helps,

Keef


------------------
Trouble is my business
------------------

Share this post


Link to post
Share on other sites
Thank you, both of you. This is encouraging, I will (try) follow your suggestions!

Share this post


Link to post
Share on other sites
As far as I''ve learned at school, the most OO-correct way is to keep all your blitting functions etc inside the class to where they belong. Always make sure the sprites do all the tasks they''re responsible for, not shove it off to some other part of the system. This goes for all classes etc. Always make them do their own stuff.
Sprites can then also house their own animation frames, and the animation function would also be inside the sprite class. Same as with the bounding box stuff, the sprite is responsible for it''s own collision detection.
If it''s OO design and programming you''re after, I suggest picking up a book on UML.

-----------------------------
Jappie
BabJap Productions

"There''s no such things as bugs; they''re just unintentional extra features"

Share this post


Link to post
Share on other sites
I was actually going to post a similar problem. When I was younger, I used to take the develop and refine approach to problems. But now I sit thinking and theorising to the nth degree instead of actually getting on with it.

When I see myself getting into that state I have to smack myself upside the head and tell myself to get on with it.

Not always the best approach but if it''s a ''just for fun'' project there''s no harm in wasting a little time redeveloping things.

Share this post


Link to post
Share on other sites
quote:
Original post by Jappie
As far as I''ve learned at school, the most OO-correct way is to keep all your blitting functions etc inside the class to where they belong. Always make sure the sprites do all the tasks they''re responsible for, not shove it off to some other part of the system. This goes for all classes etc. Always make them do their own stuff.
Sprites can then also house their own animation frames, and the animation function would also be inside the sprite class. Same as with the bounding box stuff, the sprite is responsible for it''s own collision detection.



That''s what I thought, at first. Not everything that you want to have collision detection will be a sprite. Say you have an invisible wall/block, or the edge of the screen.

quote:
If it''s OO design and programming you''re after, I suggest picking up a book on UML.



My C++ teacher had a huge book on the subject, I will check it out. Thanks for the suggestion

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Make collision detection a separate class, and have objects inherit from it. Commonly used methods can be implemented in CCollisionDetection, and overriden by those classes which have a special requirement.

Take a look at Game Design & Architecture. They walk through the design of a game engine, and I found it pretty helpful.

Share this post


Link to post
Share on other sites
I don't know much about OO, but I've done quite a bit of game engine coding, so take everything I say with a healthy does of skeptisism.

Your graphics routines will have plenty in them without being a part of the sprite class. You'll have to allocate memory, format it, load images from disk, do straightforward blits, do blits with transparency, do alpha blits, clip all the blits to another chunk of memory, and probablly various other things that didn't get mentioned.

Next, I'd probablly make a rectangle class. The obvious functionalities for rectangles are checking collision, and finding intersections. Both are useful for drawing routines, assuming you're doing everything yourself, but can be implemented in the classes that control the drawing logic.

From those two, it's about time to create a sprite and tile class. The needs of those will vary from game to game, but you're going to need drawing, clipping, and animation routines for those. If you want, you can put collision data here. And remember, any self contained entity has at least 2 possible collision areas, vunlerable areas, and attack areas. Both of these are probablly best kept separate from the rectangle for the graphics blit. edit: And just to let you know, as it's common for different types of creatures to have different collsision needs, it may be best to absract the collision data up one more level.

So, now you can draw. What next? Input! There are a lot of ways to structure this, depending on the API, and I'm feeling too lazy to go into it right now.

Anyway, I think the best way to go about coding it, is to separate everything into little chunks, put only what fits absolutely in there, and code and test one of those. Then do another logical piece. Build the parts, then put them together.

Anyway, good luck.
Hope this helped out a little

Edited by - ThoughtBubble on November 22, 2001 4:35:42 PM

Share this post


Link to post
Share on other sites
If you wanna learn this "software engineering" style. Do this:
1) List requirements you want(called customer requirements) like features such as shadows, or whatever, 2d/3d...you probably have done this in your own way so far.

2) Then go through those features you want and break them down into steps (or use cases if you wanna get technical)... you probably have done this in your own way so far.

3) Architecture: break up the problem in "Systems" and plan out how the data will interact.

4) Start coding, just practice like the others say, and have fun.

5) Once you got some features to your engine, you can add some of the major elements to your game and see how it starts working out...if something aint right, redesign.

As far as OOP/OOD... its good for some things, but sometimes i think its better to code stuff out into functions and later go "AH HAH!! This so-and-so stuff can be a class!"

Good luck.

Share this post


Link to post
Share on other sites
Hi

OOP Design is really difficult! Putting the draw method in or outside the sprite class depend on the situation.

Most time we want that the object knows how to draw itself.
But there are cases where it is better to take another approach.

For example you want to be able to use your sprite class for different operating system, different apis, ... or whatever. Then it may be useful to do it outside or at least delegate it to other classes

e.g.
class ISpriteDrawer
{
void Draw(CSprite *sprite) = 0;
};

class COpenGL :public ISpriteDrawer
{
...
void Draw(CSprite *sprite) { .. implementation for OpenGL.. };
};

class CDirectX :public ISpriteDrawer
{
...
void Draw(CSprite *sprite) { .. implementation for DirectX .. };
};

in CSprite => just delegate
class CSprite
{
Draw(ISpriteDrawer *drawer);
};

So it depends on how you want to use your classes.

Visit: www.bunnz.com
Have fun
Bunnz

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I have a Puppet class which represents a physical object. It has an x,y,radius,weight,etc... it also has a pointer to a PuppetImage (it started making a lot more sense to have a few of unrelated Image classes). Each Puppet is controlled by an Entity so the Puppet has a pointer back to it. Each frame the Entity sets the image pointer using its animation stuff.

I''m going to have an Animation class that will store PuppetImages (or actually I will template it so it works with all Image types, they are pretty similar). So each Entity will have pointers to the various Animations it uses (so they can share the same animations). In order to make things work I''m going to have each Entity store some sort of AnimationState or AnimationInteger or just some sort of int and use that to figure out which frame to set the Puppet''s pointer to.

Thus in the Puppet class I have stored: physical information and current appearance. In Entity I have a whole bunch of stuff, it will be a base class for a variety of things, it will mediate between physical representation, AI/input, animations, etc.... In Animation I have a sequence of images that can be shared.

Don''t be so obsessed with OOP or anything else for that matter. It is not a goal, it never is, and it never should be. It is just one of the many useful tools you have available to you. If your goal is to make pretty UML diagrams and please your teachers, by all means keep it up. However I think a more noble goal is to build a great game. I think that the only viable approach to making a great game involves making several bad games first. Experience makes planning easier.

Share this post


Link to post
Share on other sites
quote:
Original post by abdulla
you had a C++ teacher? damn your lucky, i have to learn everything by myself


Actually, he just read the book in front of the class and assign projects, give exams, answer questions(Ouch, most of the time it''s better not to ask any). Good thing is that he is very strict on syntax and layout. On the other hand, it kills our creativity because he doesnt want added "cool" features in our programs.

Share this post


Link to post
Share on other sites
quote:
Original post by Bunnz
OOP Design is really difficult! Putting the draw method in or outside the sprite class depend on the situation.

Most time we want that the object knows how to draw itself.
But there are cases where it is better to take another approach.

I used to look at it the first way, and assume that drawing itself was part of an object''s ''responsibilities''. But when you work out all the collaborations (ooh, UML terminology) for that, it can be more hassle than it''s worth, because now your creatures or whatever need to have knowledge of the screen, the graphics system, etc.

So I moved some of this out of the movable entities (creatures, items, etc) and into a ''DrawGame'' class or whatever. Basically, an entity knows what it looks like, but doesn''t handle the drawing. The DrawGame class gets a list of entities to draw, asks the entity what it looks like (ie. what sprite to draw) and then it handles all the drawing itself. So now all the graphical code is isolated in one class. A Creature class may know which animation set it uses, and which specific sprite to use given its current state, but it doesn''t need to know how that sprite gets onto the screen.

If you consider conceptually splitting your classes into Entities (objects that have identity, properties, and handle the low level functionality to operate) and Controllers (objects that coordinate Entities and contain your program''s higher level functionality), then it becomes a little easier. Entities mainly just contain enough info to be self-contained, as it''s the Controllers who do the work.

Of course, there''s no right or wrong way: it''s just a case of whatever seems ''cleanest'' to you, and whatever helps you make more reliable code.

Share this post


Link to post
Share on other sites
After reading numerous posts about engine design and chatting to a number of ppl at #gamedev and #flipcode I have finally got a design for my engine. The engine can support mutliple API''s.

My engine is devided into a number of stages, support, system, core, console, and interface.

The support stage is comprised of a number of classes that are used thoughout all the stages of the engine. This includes file io, matrix, vector clasess.

The system is made up of the configurator class which is really on top of all the other classes in the system stage, it loads up config files and decides the defaults for the other classes like window size, which API to use (DX or OGL). The other classes in this stage are dsound, dshow, dplay, dmusic, d3d, ogl. If the configurator chooses DX then textures will be #define texture D3DTEXTURESURFACE. and when OGL is choosen textures will be #define textures [what ever OGL uses], same for lights, etc. This way the game is API independant.

The core includes stuff like collision detection, physics, particles classes.
The console is just for debugging, and cannot part of the game, it can change game states.
The interface is basically a class combining the important class methods of each of the stages above it. So when making a game all the user does is make a interface (INTERFACE interface) and they can access all the features of the engine though it(interface->render(), interface->playsound(), interface->detectcollsion(), interface->showconsole())

Does anyone have any idea''s to improve this design. You can tell me if its flawless and I''m the best programmer you''ve seen.:-)

Thanks
flashlaserz

Share this post


Link to post
Share on other sites
>Should the blitting functions be in the sprite class ? or >outside ? I''m leaning toward inside because I guess it would >make things easier... or not.., but what about blitting the >rest of the stuff like the background, that would mean >duplicate function. :/ don''t want that. Arghh my head

This is a VERY common problem and we have all faced these decisions.

I suggest reading Chris Hargrave''s "Code on the Cob" articles however even though I agreed with 90% of what he said, I don''t like his coding practice allthough it is common. So if your a C++ junkie I would allmost recommend you don''t read the code :-)

Basically what he says is that you should ONLY use inheritance when absolutely neccessary and I categorize this into 2 situations.

1. Where the base class is abstract , use pure virtuals in C++.
And the calling class comunicates with the object though this abstract interface.

2. Where there is shared functionality that either does not use external objects or that the functionality should definately not be in the external object.

However if your unsure DON''T use inheritance.

In the case of your situation you could either:

1. Contain the blitting function in the Base class, then you would not duplicate code ( We all agree thats very bad ).

2. Pass in the graphics class into the Draw function then from there pass in members to the graphics function that does the Blitting.

I prefare option 2 as it allows changing the underlying system API more easy and seems to work better in the real world and it doesn''t "tie" the sprite to the graphics module.

The problem with 1. Is that if you have a sprite that can be used for collision detection, generating sounds, drawing etc then you will need to use Multiple Inheritance which is nasty and should be avoided.

Ironically though, your teacher would probably prefare 1. but what do they know :-), Remember you can still use Composition in OOP.

Share this post


Link to post
Share on other sites
quote:
Original post by Anonymous Poster
Every journey starts with a first step. It doesn''t matter if you trip and fall, just get up and get walking



Gold words!

Share this post


Link to post
Share on other sites
I hate to take this subject off course, but just for a second i must ask...

With respect to the person who wants to SELECT #define (directX texture) OR SELECT #define (Opengl texture) depending on config file. I was under impression that you CAN NOT DO THIS. Am i correct?

Share this post


Link to post
Share on other sites
Depends what you mean. Assuming it's C/C++, and you mean the build config, then you just do something like


#ifdef USEDX
#define 3DAPI DIRECTX
#else
#define 3DAPI OPENGL
#endif


Superpig
- saving pigs from untimely fates
- sleeps in a ham-mock at www.thebinaryrefinery.cjb.net

Edited by - superpig on November 23, 2001 4:33:01 PM

Share this post


Link to post
Share on other sites
I'' m also just working on a 2D-Graphic Engine. In my design the blitting-Functions are in the classes which represents a graphical Object that can be seen on the screen. That means that every visible Object knows how to draw itself. This is I think one of the responsibilities of a Graphic Object, but agree also to the other posters that this is depending on the situation and on the point of view you have on the system. My advice to you is to read some books about Object-oriented Analysis and Design, and about Design-Patterns (e.g. "Design-Patterns" from the Gang of Four). Design-Patterns can be very helpful for common Design-Problems, they were a great help for me in my project.
Good Luck.

Peter Gmeiner

Share this post


Link to post
Share on other sites
As others have said in this thread, don''t put your drawing functions in the Sprite class. Allowing the Sprite class to draw itself sounds like the proper OO way to do it, but sometimes speed and functionality is more important. Here are a few reasons for not putting drawing functions in your sprite classes.

1. Mulitple APIs. Putting the Blitting functions in a Graphics or Surface class allows you to derive new Graphics classes for individual APIs, like OpenGL or DirectX.

2. Client/Server Architecture. Putting the Blitting functions in the Sprite class means you''ve now tied together graphics and data, which is a Bad Thing. What happens if you decide to go with a server/dumb client approach? In a dumb client, the clients would not keep any kind of data on entities. All they are required to do is draw these textures at these locations and send all user input to the server. If the drawing functions are tied to the entities then you now require the client to, at the very least, keep dummy entities which can waste tons of memory if there are many entities.

The server only cares about keeping track of the entities, not drawing them. But if the drawing functions are tied to the entities, then the servers entities will automatically try to draw themselves, which the server doesn''t want to do. At the very least it will be required to have OpenGL or DirectX, unless you code in bunch of if/else statements to check if the user is a server or client. Very messy.

3. Memory/Speed Optimization. Although it seems that every entity should own it''s own texture, that''s a memory and speed no-no. All Ogres should share the same texture. This makes you use much less memory and can speed up drawing considerably.

Also, telling the Graphics class to draw these textures will allow the Graphics class to wait till all drawing requests have occured, sort them by texture, model, etc, then draw them all at once, thus optimizing the graphics requests, which can result in significant fps increase.


This is why some of the procedural programmers complain about OO, as many newbie OO programmers would put everything to do with sprites in the sprite class, totally black boxing everything. It makes for a new design, and this approach makes sense, but is a speed and memory killer . You need to always keep speed and memory requirements in mind when designing OO classes.


- Houdini

Share this post


Link to post
Share on other sites
I dont think letting the game objects know how to draw themselves is a problem. You just need to make sure they dont actually each hold a copy of their texture!(because as mentioned above thats a certain way to eat up memory). The textures used could be held by a "GraphicsManager" or something like that. When a new game object was instansiated it could then check the GraphicsManager to see if its texture was already loaded, if it was it then obtains a reference/pointer to it, and if its not loaded, it request that the GraphicsManager load it, and then gets a pointer/ref. to it. That way it can still draw itself, but textures will be shared.

Btw: I know next to nothing about serious game programming, but i have some experience with OOP(and Java), so if i sound like an idiot, theres your reason

Share this post


Link to post
Share on other sites
quote:
Original post by Ziphnor
I dont think letting the game objects know how to draw themselves is a problem. You just need to make sure they dont actually each hold a copy of their texture!(because as mentioned above thats a certain way to eat up memory). The textures used could be held by a "GraphicsManager" or something like that. When a new game object was instansiated it could then check the GraphicsManager to see if its texture was already loaded, if it was it then obtains a reference/pointer to it, and if its not loaded, it request that the GraphicsManager load it, and then gets a pointer/ref. to it. That way it can still draw itself, but textures will be shared.

Btw: I know next to nothing about serious game programming, but i have some experience with OOP(and Java), so if i sound like an idiot, theres your reason



Ziphnor, you are right, you definately want some sort of texture or graphics manager that passes out pointers or integer values instead of instantiating new textures.

However, if you let your objects draw themselves then you can''t just tweak your graphics dll to sort drawing by textures, thus giving yourself a potentially large spead improvement. Instead, you''d need to tweak your game code to sort your game objects by texture, which you may or may not have a negative effects in other places. Plus it just doesn''t make sense to change your game code to speed up your graphics code. You should try and keep as much graphics code in your graphics classes/dlls. Same with your input code, audio code, game code, etc.


- Houdini

Share this post


Link to post
Share on other sites
I see your point, but i cant help thinking that adding new content to a game, should be as easy as just dumping a few files into your game dir. I dont like the thought that you have to change code to add new stuff. And you *would* have to do that you had all your graphics-related code in a graphics dll.
Perhaps a GameObject could have a pointer/ref. to GraphicRepresentation object. The GR object could be in charge of handling all drawing/and loading textures in the graphics manager. Then when you wanted to draw the object you just called thisGameObject.getGR().draw( params ); or something like that. But on the other hand this really doesnt allow you to tweak graphics in one place as you (probably wisely) suggested... hmmm...
Of course you could also just have a DrawParams struct/class in each GameObject that contained the texture filename, the coords of the current frame of animation in the texture, etc which you could then pass to the graphics engines draw function when the object should be drawn(eg. engine.draw( myGO.getDrawParams() ). Thats probably smarter because, as you said, the game code itself really shouldnt need to mess around with whether or not its texture was loaded etc.
Hehe, just noticed that i now moved the drawfunction out of the gameobject itself
But one thing that is still contained in the gameobject is the animation of itself(ie it controls what part of its texture is drawn), and that i actually thinks i a good idea.
This a really interesting discussion, i think i might have to make a 2d graphics engine now, just because i cant help plan it

Share this post


Link to post
Share on other sites

  • Advertisement