7

I'm a fairly experienced programmer, but I'm still relatively new to OOP architecture and design in c++. Most of my experience is with C# and Java. I recently endeavored to code up a simple game engine in c++. I used SDL for the graphics. In this post, I would like to discuss my architecture and to get some feedback on it. Particularly, I've run into a design issue that I would like some help with. So, here goes:

  • In my main function, I initialize all of the SDL stuff for drawing to the screen, etc.
  • Then I instantiate all of the objects that I intend to use: floor, wall, player, etc.
  • Next I start the main loop. This loop executes each object's movement, collision detection and collision handling functions, and redraws them.
  • The main loop runs until the application is quit, drawing one frame each iteration.

My problem is this: I tried to do a kind of interface-style design. It involves a series of abstract base classes that allow each object to implement a behavior. For example, if I want an object to be movable, it would have to inherit from the movable base class which contains a virtual function called move() and some position coordinates. If I wanted it to be collidable, the object would inherit from the collidable abstract class, which contains the virtual functions checkCollision() and handleCollision() as well as a hitbox member variable. An object like the player inherits from both of these base classes as well as several others.

This works well enough as long as I'm doing everything by hand in the main loop. I can just say:

player.move();
player.checkCollision();
player.handleCollision();
player.draw(). 

and it's fine. But I would like to be able to have a vector or array of generic objects in the main loop and do something like this:

for each object in vector
    if object is of type movable
        object.move();
    if object is of type collidable
        object.checkCollision();

I thought that I could accomplish this with dynamic casting, but I really haven't been able to come up with anything. I've tried storing them as void pointers, but that doesn't work the way I want it to. I've been reading about this gameobject-component architecture for video games that I might try, but I'd really like to salvage what I've already written. I figure this is a good learning opportunity. If anybody has any ideas I'd really appreciate it. How does my architecture compare to other simple game engine designs? does my interface architecture make sense or is it totally wonky?

Jonatan
  • 2,734
  • 2
  • 22
  • 33
shwoseph
  • 249
  • 3
  • 12
  • Could you keep one list per interface? In your example; one list of movable objects, and one list of collidable objects? You would need to keep the same object in several lists if it has more than one property though. – Jonatan Dec 26 '12 at 22:46
  • What Jonathan says is a reasonable alternative to dynamic casting. You can make it "better" by giving every object an `enroll` method (or any name you'd like), to which you pass a registry of objects, which contain those separate lists. For instance, if you have a `FlyingSaucer` class, the `.enroll(Registry r)` method of it would then call `r.add((Movable) this)` and `r.add((Collidable) this)`, adding the `FlyingSaucer` to both the list of movables and the list of collidables. Business logic to ensure that objects are never partially removed can then be hidden into `Registry::add`. –  Dec 26 '12 at 22:52
  • You can use `typeid` operator, http://en.wikipedia.org/wiki/Typeid – Dims Dec 26 '12 at 22:54
  • @Dims: that's possible, but then you depend on the (potentially inefficient, and/or over-powerful) RTTI that comes with the compiler. –  Dec 26 '12 at 22:57
  • I think RTTI is unavoidable in the case of virtual functions you already have. – Dims Dec 26 '12 at 22:58
  • Okey, another idea: make all methods present in all classes, but let them be empty in unneded cases, so you will just call each for each – Dims Dec 26 '12 at 23:00
  • Can you have a virtual method `doStuff` in the base class, iterate over the objects, and let each object decide what needs to be done? – tmpearce Dec 26 '12 at 23:12
  • @Dims: Do you really need full-blown RTTI for vtables? Also, if you give all classes no-op methods, you not only break the idea of having interface classes, but you also introduce the expression problem. –  Dec 27 '12 at 00:18
  • @tmpearce: it seems Krozark added that answer. I'm not really keen on that idea, because you lose centralized control over processing of the various aspects of your objects. The nice thing about such control would be mostly diagnostics, such as timing how long collision checking takes. To re-introduce that would definitely make your system much uglier. Also, giving every object an `doYourThing` method obviates the need of having interface classes, and makes you repeat the same code over and over again in `doYourThing` for every seperate class. That would violate Don't Repeat Yourself. –  Dec 27 '12 at 00:21
  • @Dims: you were right about needing some RTTI for C++; I forgot about multiple inheritance ;) –  Dec 27 '12 at 10:49

2 Answers2

9

If you work in C++, try SFML, it's faster than SDL, and if you know OpenGL, you can use it too.
For your problem:

class Entity {
     //constructor and other stuff
     void virtual exec() =0; ///<= pure virtual method
};

class Movable : Entity {
    void move(); //do somthing
    void exec() {move();};
};

class Collidable : Entity {
   void col(); //do your job
   void exec(){col();};
};

std::vector<Entity*> e_v;
///push some instance
for (Entity* e : e_v)
   e->exec();
hdoghmen
  • 3,175
  • 4
  • 29
  • 33
Krozark
  • 862
  • 9
  • 27
  • `std::vector` won't do the job in C++ because no matter what type you put in it will get sliced to `Entity`. – Mark B Dec 27 '12 at 14:01
  • Sorry, I forget the "*". vector – Krozark Dec 27 '12 at 14:19
  • 1
    With C++11 or even the TR1 or boost's smart pointers the use of raw pointers is in many cases depreciated. I'd much rather use `std::vector>`. – Lukas Dec 27 '12 at 18:51
0

What about using the template method pattern and non-abstract empty virtual functions in the base class?

class Entity
{
    void update()
    {
        move();
        checkCollision();
    }

    virtual void move() {}
    virtual void checkCollision() {}
};

class Movable : public virtual Entity
{
    virtual void move() {}   // Stuff
};

class Collidable : public virtual Entity
{
    virtual void checkCollision() {}   // Stuff
};
Overblade
  • 656
  • 2
  • 7
  • 25
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • I like the virtual inheritance solution. I didn't even realize there was such a thing. I had been trying a similar approach but ran into the diamond problem. – shwoseph Jan 02 '13 at 19:50
  • Diamond of Death has been solved in games by NOT using OOP in games. Try searching for "property-centric" or "component based" architecture. –  Aug 30 '15 at 19:37