An elegant object-oriented way to design a game engine

Started by
26 comments, last by hpox 22 years, 4 months ago
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
Peter Gmeiner
Advertisement
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
- Houdini
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
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
- Houdini
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
Okay just some thoughts.

So I''ve created my design (Actually not really finished yet but anyway) and ended up with the following (Partial):

Draw prim | Graphics prim | Console | Particle | ...
Graphic | Sound | System | ...

There might be more things to include, and the whole script engine is missing but I guess this is enough to make my point.

Graphic, Sound, System and whatever (Network??) are modules who make the abstraction to the system the game runs on. That is ALL modules above this are system independent and only call functionality in the lower classes. This gives some overhead but this is unavoidable on a multiplatform/api engine.

Some of the low level modules even need double implementation since they depend on the OS and API using. Graphic is one; since you can use Win/Lnx and/or OGL/DX.

So where do I put my DrawObject method? Well it does not belong to the Graphic module, since this is basically a huge class doing 1to1 mapping of DX (at least here since I don''t do OpenGL). Draw prim (Primitive) is used to draw simple geometric objects and 2d of cause, so it doesn''t belong here either. This only leaves the Graphics prim module. And here it goes

And then you probably think that this has to be a huge class, being able to handle all kind of objects and stuff. But nope it only handles IGraphicObject* types. All objects consist of the same; points and planes (Particles and Triangles/Quad Angeles and so on). And this is what we can draw. Okay here comes the point GraphicObject isn’t a class but an interface (Abstract Base Class) hence the I and not a C. It contains no code what so ever, also the classes which implements this interface does not contain any drawing code. But they contain a structure which contains the total information on how the object is formed, and of cause a table of pointer to the textures for the object (This allows for multi pass, multi texture rendering).

So the object does NOT know how to draw itself, this might be a disadvantage at times, since you might be able to design your own super optimised code for this object. But this is seldom and a lot more can be gained by optimising the Graphics prim module. Optimising this module not only influence the object in question but also all other drawing operations. An optimisation of just 1% (for a single object) can boost performance 100% (or more ). So don’t waste your time optimising single objects.

Using this approach also allows for easy implementation of new objects, the only thing they need to do is to implement IGraphicObject. You could easily just add a script file to you game dir which gets automatically loaded at start up and voila you have a new object type in the game (This of cause assumes you are using a scripting engine). I don’t really need to do complex programming (Well not much though) in order to add new object types, because I only need to define how the data structure looks like, and the Graphic prim handles the rest.

Now you are properly thinking about animation, this is again handled by the object itself. The reason for this is simple, lets take an example:

Your hand. You can have it closed or opened, that’s up to you. But you don’t know how to draw it in the world (This is done by mother nature by reflecting light off your skin), you also don’t know anything about the light used (infrared, green, blue, or bright white) and you don’t care. The only thing on interest is to define the state it’s in the Graphic prim (Mother nature) handles the rest . There is also the thing about material, in out case skin. Should I know how this is show in the world or is it the worlds work (I hope you can guess this one ).

This takes the load of my shoulder when working with my objects, but it’s a lot of work writing the right DrawObject method, so I don’t waste clock cycles in here.

Well that was my cents worth. Guess there''s a lot of comments/criticism/questions.

-- Sturm
---------------------------------------------------Life after death? No thanks, I want to live NOW --- Sturm 2001
quote:Original post by Ziphnor
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.

I can''t help but think you''re talking about different things here. You''re gonna need your graphics code somewhere , whether it''s in a DLL or not. But if you let objects draw themselves, then the graphics functionality can''t be abstracted away from the objects. One benefit of abstraction is the ability to use DLLs, but there are others too. Whether you can replace graphical assets just by overwriting files isn''t really the issue, and is just as easy to do no matter how your graphics code is distributed.

- "Whether you can replace graphical assets just by overwriting files isn''t really the issue, and is just as easy to do no matter how your graphics code is distributed."

That wasnt what i meant, i was saying that you should be able to add(not replace) new gameobjects, without altering the graphics engine. Thats why i think that the gameobject should know which texture its using, and how its animated.
I you read to the last part of my post you will see that i actually do agree that the drawing functionality itself shouldnt be in the gameobject.

This topic is closed to new replies.

Advertisement