Is dynamic_cast the solution?

Started by
8 comments, last by HarryW 19 years, 3 months ago
Hi everyone!. I'm trying to make an entity manager to use in my games and came up with some design issues. By now, I have various kinds of entities: class Entity; // base class, pure virtual update() method. class Positionable: public Entity; // an entity with position and a move() method. class Renderable: public Positionable; // render() pure virtual method. class Collidable: public Positionable; // onCollide(Entity&) pure virtual method. A "bullet entity", for example, could inherit from both Renderable and Collidable. Currently, the entity manager has two lists, one of renderable entities and another of collidable entities. It has a method add(Entity&) which tests the types of the entity to add (using dynamic_cast) and inserts the entities into the desired lists. When the world is rendered, I call the render() method for all renderable entities, and for each pair of collidable entities <ent1, ent2> I do a collision test and call onCollide on both entities. So, my questions are: 1) Is this a good design? I heard using dynamic_cast's is signal of a bad design, but I can't think of any better. If I make my entity manager have two methods addRenderable(Entity&) and addCollidable(Entity&), I have to make two calls to add an entity which has that two properties (or I could forget one of them). 2) With this design I see collision management very easy to do. If I want to create a collidable entity, I can make it publicly inherit from Collidable, define the onCollide(), and the entity manager takes care of everything. But having the collision detection done into the entity manager object forces me to use dynamic_casts in almost all the onCollide() implementations to check what the entity is colliding with, since it recieves a reference to something of the base class. By now, I'm using this design because it allows me to create game entities with very little effort... but is this a good use of dynamic_cast's? Is there a best approach (better design) to accomplish the same tasks? Thanks to everyone, and sorry for my bad english :).
Advertisement
Oddly enough...
Quote:1) Is this a good design? I heard using dynamic_cast's is signal of a bad design, but I can't think of any better. If I make my entity manager have two methods addRenderable(Entity&) and addCollidable(Entity&), I have to make two calls to add an entity which has that two properties (or I could forget one of them).


What you would do then is make the call for an object to "register itself" in the constructor of each respective object type. That way it is impossible to forget, even if you derive a lot of things from it later on.

In my project, i keep two seperate lists, even though there are some things that could go in both. This, i think, makes the list iteration much faster, and the collision detection even faster. Collision detection objects, IMHO, need to be highly segregated and typed so that you don't end up with a "check everybody in the whole world against everybody else in the whole world" scenario.
So a Renderable object can't be a Collidable? So you can only collide against objects you cannot see?

I would suggest splitting the class-hierarchy. Make Renderable and Collidable abstract base-classes in that they do not derive from any other class.
class Renderable{public:  virtual ~Renderabel() {}  virtual void render() = 0;};class Collidable{public:  virtual ~Collidable() {}  virtual void onCollide(Collidable &obj) = 0;  // add methods that Collidable requires to correctly handle collisions  virtual void setVelocity(const vector3d &v) = 0;};

This way you don't need dynamic_cast, but you need one add(Renderable *) and one add(Collidable *).
Quote:Original post by amag
So a Renderable object can't be a Collidable? So you can only collide against objects you cannot see?

I would suggest splitting the class-hierarchy. Make Renderable and Collidable abstract base-classes in that they do not derive from any other class.
*** Source Snippet Removed ***
This way you don't need dynamic_cast, but you need one add(Renderable *) and one add(Collidable *).


As a suggestion to this, I'd rename your classes so they begin with an I instead of a C or no leading character, C generally implies a concrete class you can instatiate while I implies and interface, ie a class that has to be derived from and implemented.

Cheers
Chris
CheersChris
in direct answer to your question is probibly not because dynamic_cast is rather slow and should not be used in games and least of all in an extreamly fast object like a bullet that must be near spontaniously generated. I dont know if dynamic_cast would even solve your problem im just giving you the over all answer in tearms of game design

if you still need dynamic_cast there are other ways around it so just let me know

[grin]
____________________________"This just in, 9 out of 10 americans agree that 1 out of 10 americans will disagree with the other 9"- Colin Mochrie
Quote:
...least of all in an extreamly fast object like a bullet that must be near spontaniously generated...

Uhm, there seems to be some misconception of the speed of dynamic_cast. dynamic_cast doesn't take seconds to execute if that's what you think. Speed-wise it's perfectly acceptable to use something like dynamic_cast in this case (unless you add maybe thousands of objects every frame (in which case you get other problems with speed)), however from a design point of view, it can be a sign of an incorrectly designed class-hierarchy (as I would argue it is in this case).
A virtual function call is going to be cheaper than a dynamic_cast. I'd prefer to use polymorphism - add IsCollidable() and IsRenderable() to the base class and override them in the derived classes to return the relevant value. If that doesn't fit into your solution, perhaps a GetClassID() method which returns an enum associated with that class, but that's really a bit of a cheap hack to avoid having to generate proper RTTI.

The IsCollidable()-type solution is preferable, obviously, as it allows you to add new classes without changing the way you manage them, although you have to be a bit careful that you don't end up with IsSomething() methods percolating up to the base class's interface whenever you add new derived classes. It's probably better to have the instances add themselves to the appropriate list(s), or something similar.

There is an OO law/rule/principle that I've come across that states that wherever you are using an instance of a class C, you should be able to replace it with an instance of a class D derived from class C. I've forgotten the name, though, which isn't much use.
Harry.
Quote:
I'd prefer to use polymorphism - add IsCollidable() and IsRenderable() to the base class and override them in the derived classes to return the relevant value.


Actually an IsXYZ() method isn't going to be much use, of course it will help to put the object in the right list (without dynamic_cast) which is good, but then you need to add (abstract) methods in the base class to handle rendering and collision detection/response and whatelse, which means that you don't really need a class hierarchy at all, why not just have one base-class?

And if you still want one (like that) it won't be very scalable since when you need to add PlayingSoundable and Serializable, you need to change the base-class. In this case it's better to use interfaces or in C++ multiple inheritance from abstract base-classes.
I think that's more or less what I said... there's a definite maintenance issue and it's not the prettiest way to create a class hierarchy. Still, though, if you have a set of classes defining 'entities' in the game (so, maybe they're all derived from the class Entity so you can put them all in an array), where those that are renderable inherit from IRenderable, and those that are collidable inherit from ICollidable, how do you neatly take a pointer to an instance of Entity and use it to get a pointer to an instance of IRenderable, to actually do the rendering?

This seems to me to be more of an issue with the way the objects are being used, rather than the way classes are defined. If instances of classes derived from Collidable registered themselves with a collision manager, and instances of classes derived from Renderable registered themselves with a rendering manager, then you'd have a list of objects of the appropriate type for each manager. The same sort of approach is equally valid with an aggregation strategy, of course.

Incidentally, now I look at the original post again... you have a 'deadly diamond' there, which is not very healthy.
Harry.

This topic is closed to new replies.

Advertisement