🔒 Locked
Game Engineering
Advertisement
Original Post
I've had similar questions on here before, but I still haven't had a major breakthrough. I've been programming in C++ with the structured paradigm for years. I really want to upgrade to OOP, and gaming seems like a great reason to start. I've searched the forums, and sunandshadow had a similar thread on arranging classes. I am still having difficulty working out the details. I'll boil it down to my most current problem; I have a CSprite class which keeps track of DirectDraw surfaces and RECTs to blit from. I have a CGraphics class that contains the directdraw objects and the surfaces pointed to by the CSprite class. The CGraphics class has a method Blit(), to which I pass the pointer & Rect for the source, as well as the destination Rect. Main question Is this good OO Design? How should objects interact? I know I should keep the particulars of each sprite contained in the sprite class and the vector of DD surfaces in the Graphics object, but what is considered "good design"? Oh, and I'll be out for a couple of hours, so I'll just thank you for your help in advance so as not to be rude! [smile]
I understand what you're trying to say, but it's really just the details that get me. I've actually taken a few classes on Systems Analysis & OOP Database Integration, but they didn't provide any concrete code examples.
I've seen more entity diagrams and flow charts than I care to ever see again, but I'm still not sure how to model "relationships" between objects.
Should my Sprite have a draw function that takes care of the blitting and all that, or is my CGraphics' Blit class enough?
What I mean is, what is the proper way to model these kinds of operations in OOP?
I've seen more entity diagrams and flow charts than I care to ever see again, but I'm still not sure how to model "relationships" between objects.
Should my Sprite have a draw function that takes care of the blitting and all that, or is my CGraphics' Blit class enough?
What I mean is, what is the proper way to model these kinds of operations in OOP?
the "proper" way is whatever way WORKS and accomplishes the goals you set out to achive.
If your only goals are "my game works and is fun to play", then who cares how you accomplish that goal?
If you set as a goal "my code is easily understood by others" then OOP might help you get there. When you create a class, say "is the prupose of this class easily understood?"
If one goal is "it is easy to change and refactor my code to accomidate future changes". Ask yourself "is this class structure or this design paradigm flexible?"
Clearly the problem you are having is not "what is a good OOP design for a game engine" it is "why do i want to use OOP (other than because I am told it is good)" and "how do i go about employing OOP effectively"
You should download some sample applications that are considered good examples of OOP and try to learn more about the methodology.
If your only goals are "my game works and is fun to play", then who cares how you accomplish that goal?
If you set as a goal "my code is easily understood by others" then OOP might help you get there. When you create a class, say "is the prupose of this class easily understood?"
If one goal is "it is easy to change and refactor my code to accomidate future changes". Ask yourself "is this class structure or this design paradigm flexible?"
Clearly the problem you are having is not "what is a good OOP design for a game engine" it is "why do i want to use OOP (other than because I am told it is good)" and "how do i go about employing OOP effectively"
You should download some sample applications that are considered good examples of OOP and try to learn more about the methodology.
The way I would do it would be to create a virtual base class 'IRenderable' with a static variable of type CGraphics. IRenderable has 2 public virtual functions Render() and Update() and a protected interface to CGraphics oGetGraphics(). You could probably set up a static function call to initialize your CGraphics object, then every time you call Render(), you use the oGetGraphics() function to access the CGraphics object. If the interfaces are the same you could make DDraw, OpenGL, and Software version of the CGraphics class, of course in that scenario you'd need to do some polymorphic funkiness, but that's what OOP is all about.
#include "CGraphics.h"//Instantiating this class may result in deathclass IRenderable{public: virtual void Render()=0; virtual void Update()=0;protected://This makes so it can be accessed by derived classes only static inline CGraphics& oGetGraphics(){return m_oGraphicsInterface;};private://This is an extreme example, but you can use accessors to limit how//derived classes interact with your static and base class members static CGraphics m_oGraphicsInterface;};class Sprite : public IRenderable{public: inline void Update(){(...code here)...}; inline void Render(){oGetGraphics().Blit(m_rect);};private: CRect m_rect;};//etc...
Thanks for the help, guys.
@SnprBob:
I think you're right about asking better questions. I'm not sure how to employ OOP effectively. I can get pretty good ideas of what should be a class, but making them interact is where I'm a bit fuzzy. This RPG is mostly a programming exercise, so I'd like to learn as much about the OO paradigm as possible in the process.
@Horatius:
I recognize what you're doing to be an Interface, but I still wonder what the Interface is for. My CGraphics class contains all of the DDraw code. Are you saying that the Interface should be the accessible face for the Graphics object?
I'm confused by your class name, though. IRenderable suggests it is a renderable object analagous to my CSprite class. If that's the case, why would I want it to contain the class that manages the graphics?
I'm sorry if I seem dumb, but I just haven't had that "Eureka!" moment with OOP yet.
@SnprBob:
I think you're right about asking better questions. I'm not sure how to employ OOP effectively. I can get pretty good ideas of what should be a class, but making them interact is where I'm a bit fuzzy. This RPG is mostly a programming exercise, so I'd like to learn as much about the OO paradigm as possible in the process.
@Horatius:
I recognize what you're doing to be an Interface, but I still wonder what the Interface is for. My CGraphics class contains all of the DDraw code. Are you saying that the Interface should be the accessible face for the Graphics object?
I'm confused by your class name, though. IRenderable suggests it is a renderable object analagous to my CSprite class. If that's the case, why would I want it to contain the class that manages the graphics?
I'm sorry if I seem dumb, but I just haven't had that "Eureka!" moment with OOP yet.
IRenderable is simply a set of all objects that will be rendered to the screen
In order to render you need access to the graphics code
The static variable is sort of like a global for all IRenderable objects
The static function provides you access, since only static functions can access static objects
Interfaces establish how you can manipulate information, so the interface allows access to your graphics object, and defines how you access it and what you can do with it.
Though I do agree with the post about Java, it will teach you OOP, and they have some neat ideas about how implement certain things.
In order to render you need access to the graphics code
The static variable is sort of like a global for all IRenderable objects
The static function provides you access, since only static functions can access static objects
Interfaces establish how you can manipulate information, so the interface allows access to your graphics object, and defines how you access it and what you can do with it.
Though I do agree with the post about Java, it will teach you OOP, and they have some neat ideas about how implement certain things.
Quote:
but I'm still not sure how to model "relationships" between objects.
Seems like you're missing a key point. You're not supposed to.
Granted, this is a rather subtle point, since most every "OOP" example doesn't do it. Every inheritance tutorial ignores it. Every example class is completely ignorant of it.
Horatius' made mention of it, but let's see if I can't explain a little further.
Objects in modern OOP are best when they're not objects. Not solid 'game world' objects. Inheritance and C++ typing makes that sort of orginization hard. It's much easier, powerful and useful to arrange classes around an interface, or a property like being renderable.
At that point, a class represents a solution to a focused problem. Such solutions can better act independantly, meaning these relationships no longer exist or are cleaner. Making a class that is say.... both renderable and 'movable' is then simply a matter of inheriting from both smaller classes. Nice and clean.
For other situations this sort of setup is good:
A / \ B C / \ / .........Solutions B and C should know nothing of one another. Solutions B and C should know nothing of A. Solution A knows about B and C [and down the tree]. Or at least the interface to B and C. At this point A takes care of all of the interactions between B and C.
In more real-life examples, A would be somehing like 'physics' which would need to know about B and C to provide collision detection. B and C though shouldn't handle collision detection, merely provide an interface for A to manipulate. Thus things in the tree manipulate things lower on the tree. The objects themselves merely provide a interface, much cleaner.
The STL is a good example of this setup. Grouping is done using the container classes. Do you tailor each class stored there to make it work with the container? No, the container manipulates the other classes via a common interface [in the STL's case, constructor/copy constructor...].
And I'm not sure this is a good example, but here's some code I recently doodled up to do kinda what your example involves, even though it's perhaps a terrible example of what I just rambled about...
class basero{private:protected: int visible; caching_rect_generator *tblr;public: virtual int is_visible(){return (visible);} virtual void visibility(int v){visible=v;} void toggle_visible(){if (visible){ visibility(0); }else{ visibility(1); }} virtual void render(){if(visible){rendertree();}} void deltree(); void rendertree(); virtual void push_top(basero *); basero *parent; list<basero *> children; virtual rect bbox(){return(tblr->fetch());} virtual void rerect(caching_rect_generator *rt){tblr->del(); tblr=rt;} virtual depended_rect_generator *dependable(){return(tblr->dependable());} virtual void tblrreset(){tblr->reset();} virtual DWORD color(){return(0x00000000);} virtual void color(DWORD c){} keybinding kb; mousetarget mt; virtual void immediate_close(){delete this;} virtual void close(); basero *me(){return(this);} void no_tree_delete(); virtual void no_tree_close(); virtual void extract(); basero(caching_rect_generator *t):tblr(t),parent(0),visible(1){} virtual ~basero(){ if (parent){ extract(); } parent=0; deltree(); //delete tblr; tblr->del(); }};class ro_root: public basero{private:protected: storega on_init;public: IDirect3DDevice9 *d3ddev; IDirect3D9 *d3dif; D3DPRESENT_PARAMETERS d3dprops; d3d_vb *vb; virtual void push_top(basero *); void init(HWND winobj); void render(); int display(); int reset(){return (1);} ro_root(guiaction *oi=makega(noop,0,0)):basero(0),on_init(oi){}; ~ro_root();} *guiroot=0;class ro_image: virtual public basero{private:protected: d3d_tq tq; IDirect3DTexture9 *tptr; DWORD clor;public: virtual DWORD color(){return(clor);} virtual void color(DWORD c){clor=c; tq.color(c);} virtual void render(); virtual ~ro_image(); ro_image(caching_rect_generator *rg):basero(rg),tq(rect()),tptr(0),clor(0xffffffff){} ro_image(caching_rect_generator *rg, DWORD c):basero(rg), tq(rect(),c), clor (c), tptr(0){} ro_image(caching_rect_generator *rg, d3d_imagerc *i):basero(rg), tq(rect(),0xffffffff), clor(0xffffffff){ if (i==0){ // failed imagerc. // TODO: use some default failure image. // for now, null is fine, simple white box will suffice. tptr=0; }else{ tptr=loadtexture(i->rc,&tq); } }};basero is essentially the 'renderable' class; alas with far more than it should. ro_root seems to be similar to the common graphics class, storing d3d info. ro_image seems similar to your sprite class, which stores a pointer to the texture [which a seperate manager handles and divvies out].
The important point is the things you don't see, other classes which impliment the basero's interface. For example, in your original thread, how would you impliment a class which draws text? That won't use a surface/rect.
Hope this has helped a little.
for as long as I've been programming, I haven't came across such a neat and purely simple example made by one of contributors for Game Programming Gems 5. He wrote an article about the Component Design, and I believe that it's the only way to go when designing highly complicated programs like games. It introduces you to the way where you can easily change functionality of the class without deriving it from some other class, but rather using it INSIDE the class as a member variable and exploiting its functionality. Of course, it lacks the ability of virtual inheritance, but hey, nobody restricts us using interface classes with component design, since interface classes only provide us with the way of using provided information.
So, in case with your question, templewulf, I'd suggest you follow the Component Design (e.g COM or DCOM?). It will make your life a whole lot easier, and you don't have to worry about object interaction because you'd have pointers to each of them and an ability to query their functionality. So, if you would want to make an object moveable and renderable, you simply add interface pointers for IMoveable and IRenderable in your object, and away you go!
So, in case with your question, templewulf, I'd suggest you follow the Component Design (e.g COM or DCOM?). It will make your life a whole lot easier, and you don't have to worry about object interaction because you'd have pointers to each of them and an ability to query their functionality. So, if you would want to make an object moveable and renderable, you simply add interface pointers for IMoveable and IRenderable in your object, and away you go!
Sound like you need to have a look at a Design Pattern book. There are two that come to mind.
This is the traditional must have book.
This one is had a really good approch and makes each pattern appear useful. Good book!
Then this one is more just on object architecture rather than Design Patterns.
Design Patterns in general will broaden the way you think of how objects are architected and communicate with one another. Learning them has opened up a vast amount of ideas for me.
Charles P.
This is the traditional must have book.
This one is had a really good approch and makes each pattern appear useful. Good book!
Then this one is more just on object architecture rather than Design Patterns.
Design Patterns in general will broaden the way you think of how objects are architected and communicate with one another. Learning them has opened up a vast amount of ideas for me.
Charles P.
You might want to check out Object Mentor web site(www.objectmentor.com) and read some articles there, for example those:
http://www.objectmentor.com/resources/listArticles?key=topic&topic=Design%20Principles
and these:
http://www.objectmentor.com/resources/listArticles?key=topic&topic=Object%20Oriented%20Design
If you are new to OOP they might be a bit advanced, so read them a few times until everything becomes clear.
http://www.objectmentor.com/resources/listArticles?key=topic&topic=Design%20Principles
and these:
http://www.objectmentor.com/resources/listArticles?key=topic&topic=Object%20Oriented%20Design
If you are new to OOP they might be a bit advanced, so read them a few times until everything becomes clear.
Thanks a lot, everyone! That helps out in general, but I'm still having a hard time visualizing. I was thinking about ordering Design Patterns from Amazon a couple of weeks ago, but I have no extra cash! [sad]
I'll check out the objectmentor site in the mean time. I like the idea of OOP, but it's so hard to break away from structured habits!
I think one of our GDnet patrons should contribute an article relevant to this subject. Program Architecture in OOP! Not one of those 3 paragraph articles like "initializing DirectX for the nth time, even though it's on msdn!"
I think I might just have to get ahold of the cash to get that book, but here's hoping someone better than me will put together an article! [smile]
I'll check out the objectmentor site in the mean time. I like the idea of OOP, but it's so hard to break away from structured habits!
I think one of our GDnet patrons should contribute an article relevant to this subject. Program Architecture in OOP! Not one of those 3 paragraph articles like "initializing DirectX for the nth time, even though it's on msdn!"
I think I might just have to get ahold of the cash to get that book, but here's hoping someone better than me will put together an article! [smile]
http://www.mindview.net/Books/TIJ/
http://java.sun.com/docs/books/tutorial/java/concepts/object.html
http://mindprod.com/jgloss/objectoriented.html
After you'd read all this and have you brain sufficiently brainfucked, take a deap breath and read this. Object oriented programming is useful mainly for easy work with large blocks of your code. For this reason Java, or C# forces you into encapsulating all of your code in a some kind of a class structure.
However consistent and a clean OO code needs time to learn (by yourself not by books, books don't know your codebase. Of course you could get some inspiration from sourceforge.net)
Why do you would like to have a Sprite class? Why should Sprite class have any information about drawing surfaces? Shouldn't be Graphics class able to work with any class extending Sprite, and work correctly using informations provided by Sprite? What would happen if Graphic class would be significantly reworked, and abstracted further would Sprite class have still its functionality?
These are questions you should be able to answer. Or you might like to have things first working then OOP.
When I was accidentally unloged, I recommended you to look into Java standard library documentation, at class called Graphics2D. Work with this class often ends with just calling
Graphiscs2D gr2D...
gr2D.drawImage(image, 0, 0, null);
Shape something = new something(inicialize it);
gr2D.draw(something);
something.setLines(Lines);
gr2D.draw(something);
Some terms you might like to use a lot.
Abstract class, a class with one or more abstract method that can't be instantialized. But can be extended and abstract method would be defined in extending class.
Interface. a definition of methods/variables/constants that are guarenteed to be available in classes that implements interface.
RPG?
a standard behaviour for OO orientated design is
class weapon extends equipment implements killingDevice {
Or class xxxx extents Being {....
class itemManagement { //of course this is butt ugly name for this class.
public int addToInventory(equipment e)...
public Damage useWeapon(target t)...
And so on. Not everything is exactly effective, but you'd have a free arena to do your worst.
The problems what you would try to solve are often discused in (google groups / usenet) roguelike.development group. Also Tyrant is a roguelike that was writen in Java thus it's very likely OO oriented. (I didn't look at it's code so I don't know if it's a standard sourceforge.net like code, or if it's readable.)
http://java.sun.com/docs/books/tutorial/java/concepts/object.html
http://mindprod.com/jgloss/objectoriented.html
After you'd read all this and have you brain sufficiently brainfucked, take a deap breath and read this. Object oriented programming is useful mainly for easy work with large blocks of your code. For this reason Java, or C# forces you into encapsulating all of your code in a some kind of a class structure.
However consistent and a clean OO code needs time to learn (by yourself not by books, books don't know your codebase. Of course you could get some inspiration from sourceforge.net)
Why do you would like to have a Sprite class? Why should Sprite class have any information about drawing surfaces? Shouldn't be Graphics class able to work with any class extending Sprite, and work correctly using informations provided by Sprite? What would happen if Graphic class would be significantly reworked, and abstracted further would Sprite class have still its functionality?
These are questions you should be able to answer. Or you might like to have things first working then OOP.
When I was accidentally unloged, I recommended you to look into Java standard library documentation, at class called Graphics2D. Work with this class often ends with just calling
Graphiscs2D gr2D...
gr2D.drawImage(image, 0, 0, null);
Shape something = new something(inicialize it);
gr2D.draw(something);
something.setLines(Lines);
gr2D.draw(something);
Some terms you might like to use a lot.
Abstract class, a class with one or more abstract method that can't be instantialized. But can be extended and abstract method would be defined in extending class.
Interface. a definition of methods/variables/constants that are guarenteed to be available in classes that implements interface.
RPG?
a standard behaviour for OO orientated design is
class weapon extends equipment implements killingDevice {
Or class xxxx extents Being {....
class itemManagement { //of course this is butt ugly name for this class.
public int addToInventory(equipment e)...
public Damage useWeapon(target t)...
And so on. Not everything is exactly effective, but you'd have a free arena to do your worst.
The problems what you would try to solve are often discused in (google groups / usenet) roguelike.development group. Also Tyrant is a roguelike that was writen in Java thus it's very likely OO oriented. (I didn't look at it's code so I don't know if it's a standard sourceforge.net like code, or if it's readable.)
How bout: classes should only modify their own variables?(Apart from java-bean style classes)
eg Render needs to draw, therefore it needs to modify its draw list, so it asks all the object classes for their position/models etc.
Where as a World class which has the cameraPos/Dir variables needs keyboard input to move/modify them, so it asks the Input class if the key is pressed.
I've recently came up with this concept, does anyone think that this is a bad idea?
..edit I thought this is a good idea, because if someone else is searching through the code, they don't have to use a 'search' to find where the variables are being modified from, as I've had this problem before in group projects.
eg Render needs to draw, therefore it needs to modify its draw list, so it asks all the object classes for their position/models etc.
Where as a World class which has the cameraPos/Dir variables needs keyboard input to move/modify them, so it asks the Input class if the key is pressed.
I've recently came up with this concept, does anyone think that this is a bad idea?
..edit I thought this is a good idea, because if someone else is searching through the code, they don't have to use a 'search' to find where the variables are being modified from, as I've had this problem before in group projects.
Quote:
Original post by cparcell
This one is had a really good approch and makes each pattern appear useful. Good book!
http://www.amazon.com/exec/obidos/tg/detail/-/0596007124/qid=1127016101/sr=2-2/ref=pd_bbs_b_2_2/102-4513809-0172909?v=glance&s=books
I just want to chip in to recommend this book. Sure, the style seems really silly (and it is), but the writing is excellent and it does an amazing job of getting the point across. The downside is that all of the code is in Java and, to a certain degree, the book expects that you're programming in Java. Still, if you're very familiar with C++ this shouldn't be a big deal - it's easy to look at the Java code and recognize how to represent it in C++.
Most importantly, though, is that this book was full of OO design "Eureka!" moments for me. I actually thought I had a good handle on this stuff, but it seemed like every chapter made me realize something I completely missed. It's an excellent, excellent book and I can't recommend it enough.
Quote:
eg Render needs to draw, therefore it needs to modify its draw list, so it asks all the object classes for their position/models etc.
Well, that was the problem I had! How does one model the relationships between objects? Well, according to Telastyn, you're not supposed to!
Now, I'm pretty familiar with relational databases, so I'd be inclined to do just the opposite. The thing is, Telastyn has answered so many of my questions by now, that I just have to give him the benefit of the doubt!
I general concept that I've been getting lately is that objects should carry out their own functions through references to other managers.
I think that my CSprite object, say sprPicture would have a method CSprite::Draw(). Instead of Sending sprPicture's members to CRenderer (graphics manager), you just call sprPicture.Draw().
To do that, you have to put a reference to your CRenderer inside CSprite. That way, you use CRenderer without ever having to expose CSprite to the rest of the world.
Does that sound about right to everyone else?
Heh, I didn't mean it that literally. Still, that's getting closer.
It might be better if you define an interface for ::Draw(), then inherit the sprite from it. Then whatever calls the interface can effectively ignore the Sprite class too. The sprite class then has no relationship, only it's interface does.
It might be better if you define an interface for ::Draw(), then inherit the sprite from it. Then whatever calls the interface can effectively ignore the Sprite class too. The sprite class then has no relationship, only it's interface does.
Well I do my joins like so:
Well this is the design that ive been using the most.
When I first was trying to decide on the best class design, i was extremely influenced by relational databases :)
But they are kind of diffrent things, although a bit similar.
I think to model the relationship, you might draw lines eg from Render to World, saying draw objects,
A line from world to input saying control units.
Although in reality the design is really a star, i think you should just show why one class is accessing another.
class Main {private: Render render; World world; Input input;public: Main(); Render* getRender(); World* getWorld(); Input* getInput();};class Render {private: Main *main;public: void init(Main *main); void doSomething();};void Render::init(Main *main) { this->main = main;}void Render::doSomething() { //then u can access stuff like this: main->getWorld()->aFunction();}//then same for the other classes.//..Main::Main() { render.init(this); input.init(this); world.init(this);}Well this is the design that ive been using the most.
Quote:
Original post by templewulf Quote:
eg Render needs to draw, therefore it needs to modify its draw list, so it asks all the object classes for their position/models etc.
Well, that was the problem I had! How does one model the relationships between objects? Well, according to Telastyn, you're not supposed to!
Now, I'm pretty familiar with relational databases, so I'd be inclined to do just the opposite. The thing is, Telastyn has answered so many of my questions by now, that I just have to give him the benefit of the doubt!
When I first was trying to decide on the best class design, i was extremely influenced by relational databases :)
But they are kind of diffrent things, although a bit similar.
I think to model the relationship, you might draw lines eg from Render to World, saying draw objects,
A line from world to input saying control units.
Although in reality the design is really a star, i think you should just show why one class is accessing another.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement