OOP design questions...

Started by
6 comments, last by graveyard filla 20 years, 1 month ago
high, i posted this in For Beginers, but only got 2 replies and then the thread disapeared. so i figured it would be better for me to post in this forum. anyway, please dont yell at me for cross-posting, if this is bad, ill delete the thread. but id really appreciate some help anyway,im pretty new to programming, only been doing it for about 4 1/2 months. anyway, im working on my third game (a pacman clone), and the game is starting to get huge. its probably close to 2000 lines of code, 6 pages of source, etc, etc. ive learned a lot from coding this game, the most important thing ive learned is that you MUST PLAN things out before you code. i didnt plan a single line of this code, one day i just started coding, and then added more stuff, more stuff, more stuff. this is bad!! anyway, this reflects in my design, and when i make my next game, im going to plan everything out in the begining. now heres my design questions: is it bad to EVER have a function / data member of a class, that a certain instance of that class will NEVER use. for example, in my pacman game i have a class called Entity. Entity has things like, xPos,yPos, frame_count, etc etc. this class is for my player and the enemies. but, some funcions of this class are ONLY for the player, and some are ONLY for the enemies. functions like : Draw_Scared() // only for the enemy, obviously Draw_Player_Dead() Draw_Enemy_Dead(), ETC.... is this bad design? obviously my player will NEVER use Draw_Scared(), and my enemies will NEVER use Draw_Player_Dead(). in situations like this, should i have 2 seperate classes, one called Player, one called Enemy? if i EVER have a function/data member that a specific instance of that class will NEVER use, is this a reflection of bad design? (i havent learned inheritence yet, but im sure this will be the solution to this problem, if in fact is IS wrong to do this) also, what about mixing up global functions with class functions? IE, should a class EVER call a global function? if i had a global function called Blah();, should i ever call Blah() from inside of one of my Entity's functions? is this also a sign of bad-design? obviously global functions should make use of class's, but what about the other way around? should i keep everything encapsulated to the class, and never have a class use "outside" parts? if i EVER have a class function that calls a global function, is this a sign of bad design? what about one class using another class's functions? is this also a sign of bad design? also, if you guys have any other OOP design tips, please let me know. thank you for any help!! [edited by - graveyard filla on March 18, 2004 4:01:58 PM]
FTA, my 2D futuristic action MMORPG
Advertisement
Wow, heh, I mean this in the nicest of ways but you need to relax...
Of course it''s very important that you plan ahead. Nice design is also very...nice. But good design is something that takes a long time to learn (usually) and you learn by doing (mistakes). So don''t panic if your design isn''t perfect, in many (most?) professional cases it isn''t either... Besides what some people see as good design other may view differently.

To answer your first question though; if you know at compile-time that a certain instance will never use a certain set of methods then that''s a typical sign that those functions doesn''t belong to that class, like in your case. What I recommend is that you split your Entity-class into two classes (to begin with), one Player-class and one Enemy-class. It can be good to have both those classes derive from a common base-class, i.e. Entity (or whatever you like to call it). But if you have trouble grasping the concept of inheritance you can wait with this.

To answer your second and third question in a short way; no it''s not bad design for a method (class function) to call a global function or a method of another class. In fact it''s essential that you do, otherwise you won''t have much of a program.

About tips for OOD/OOP, I suggest you familiarise yourself with the different concepts of OO, such as inheritance, virtual functions, constructors, destructors (for those languages that apply), etc. Also make sure you learn how to use them. Finally don''t believe that everything should be OO, some concepts fit very well into an object model, some don''t. So don''t be afraid to use other design strategies where you see fit. All this will of course take years of practise, but you''ll hopefully have a lot of fun while learning.

Good luck!
EDIT: Above post is better and I asumed your using C++

Why not have a single generic draw function (or an over-rideable virtual one) to draw all states.
For example, you could have
void Draw(int state){    switch (state) {        case STATE_ENEMY_DEAD:            /* Draw Enemy Code */        break; case STATE_PLAYER_DEAD:           /* Draw Player Code */        break; default: break;    }}  

or
void Draw(int state){    // Assume that DrawBitmap draws a bitmap somewhere.    DrawBitmap(BitmapImage[state]);} 


where BitmapImage is an array of Bitmaps.
and then do this:
enemy.Draw(STATE_ENEMY_DEAD);
player.Draw(STATE_PLAYER_DEAD);

Another way would be to do something like this:

class CCharacter{private:    IMAGE* Images[3]; // You will probably want to dynamically allocate this.    int State; // Used to determine which to draw.public:    CCharacter(IMAGE* normal, IMAGE* scared, IMAGE* dead)    {        Images[0] = normal; Images[1] = scared; Images[2] = dead;    }    ~CCharacter() {/*Destroy the images here*/}    void Draw()    {        // Now Draw Image[State]    }    void SetState(int s) {State = s;}    int GetState() {return State;}    static const STATE_NORMAL = 0;    static const STATE_SCARED = 1;    static const STATE_DEAD = 2;};...CCharacter Player(ImagePlayerNormal, NULL, ImagePlayerDead);Player.SetState(CCharacter::STATE_NORMAL);// No scared player image.// Now at a later stage, draw.Player.Draw();  


The above is the way I would do it if I didn't want to use inheritance. Of course, I would use something more flexable than a static array of images, but I hope you see what Im getting at.

Alternatively you could do something like this (though this uses inheritance, which you said you haven't learnt yet...):
class CCharacter{public:    CCharacter() {}    virtual CCharacter() {}    virtual void Draw()=0;};class CPlayer: public CCharacter{public:    void Draw() {/* Draw Player Code goes here*/}};class CEnemy: public CCharacter{public:    void Draw() {/* Draw Enemy Code goes here*/}};CPlayer Player;CEnemy Enemy;...Player.Draw();Enemy.Draw();  


Which way you would do it depends on how much C++ you know, though none use any really advanced stuff.
Others may have better ways than I and I probably wrote horribly ugly code but I hope you learn something from it nontheless.


[edited by - issch on March 18, 2004 6:09:33 PM]
your thread didn''t disappear it''s right here:

http://gamedev.net/community/forums/topic.asp?topic_id=213917

if you want to find your threads, enter the forums area and click on the profile link at the top of the page (near register, bookmarks, active topics, etc). then click "view your profile" then you can see all the recent threads you''ve started and replied to.

-me
Yeah, don't worry too much about design for now. If you're starting, getting it to work is already a big step, regardless of how ugly you think that your code is. Design is part experience, part innate ability, I think. Computer science isn't really an engineering discipline yet because we just don't know what is "the" right way to approach a given problem. It's more like a craft. You learn from your mistakes, and by discussing it with people around you.

[edited by - Cedric on March 18, 2004 7:41:17 PM]
Sounds like analysis paralysis

Up front design was popular in the Waterfall methodology. You'd have an analysis phase, a design phase, a coding phase, a testing and bug fixing phase then release.

There are many other methodolgies, and waterfall has come to be acknowledged as difficult.

Check out some of the articles here to get an idea of how to design as you go, to be free to refactor, etc. See other articles like the one on Waterfall methodologies, especially Iterative and Incremental Development parts II and III.

[edited by - petewood on March 19, 2004 6:49:14 AM]
With an iterative methodology, you still do all of those phases, you just plan on doing them many times (as opposed to just once, i.e. Waterfall).


OOP is a tainted term, research OOD, Object Oriented Design.
There''s a book called "Design Patterns" which is a catalog of small (reoccuring) pieces of a design, which they call patterns (the fully qualified name is Object Oriented Design Patterns).
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
global function are only truely bad due to namespace pollution .. throw them in a namespace and they are no worse than "class" or "static" functions that have been used since the original SmallTalk ...

as for base class having function which really belong in derived classes ...

first the disclaimer - design serves a function ... to AID development ... try to never let your design hamper your development. as you learn, you should become better and better at design, and therefore, at any moment the "right" solution for GETTING SOMETHING DONE, depends on your current talents and understanding - don''t fret over design flaws that aren''t biting you.

that said however:

in an ideal case, base classes should not have derived class specific knowledge at all ... their are design methods you will learn that make this unneccesary ...

for your particular example .. the case is to have a virtual Draw() function, and have each class implement Draw() based on it''s internal state ... so the Enemy might be "dead" and if so it''s Draw() draws it dead ... and a player might be "scared" and if so it draws that way ... but their is not a unified property for the player and the enemy, if only one may be scared and only the other may be dead (man I''d love not to be able to die) ...

The key is, the virtual function in the base represpents the FUNCTION as viewed from the main programs desire ... such as DrawObject(), MoveObject(elapsedTime), SaveToStream(stream) etc ...

and then each class has custom state (if it needs to) and custom implementation of those virutal functions (if it needs to) and any custom functions it needs to help out ... then it "does the right thing" - FOR IT - when the virtual functions are called ...

for example, in my SciFi game, my:

IGameObject - is serializable (save / load to stream), and able to be looked up by ID in the central GameObjectManager.

IPhysicalObject - is a game object which adds a position in the physical world.

IDynamicObject - is a object but must also be updated based on time passing, cause it may change locations, etc.

so you see my Fleets would be Dynamic Physical objects, but my Solar Systems are just PhysicalObjects and my Technologies are not Physical at all.

I also have classes layered in their for implementing the program, such as:

IScreenObject - something which should be drawn on screen .. with guess what ... a Draw() function

so you see, the key is, these objects are derived from the types / interfaces that make sense, and only have the funtions which are valid for them ... then they get inserted (usually at creation or shortly thereafter) into the right lists that make the game "go" ... like the list of VisibleScreenOjects, that calls Draw() on each .... the list of DynamicObjects that calls Update(timeElapsed) on each ... etc ...

hope this gives you some ideas ... and good luck

This topic is closed to new replies.

Advertisement