Skip to main content
GameDev.net gamedev.net
🔒 Locked

class inheritance

Started by a38750 Sep 16, 2000 at 1:05 PM 1 replies 300+ views
Original Post
a38750
a38750
I''ve got an Entity class, and several other clases derived from it. In particular, an ItemEntity class for pickups etc. Now in my player class, I have a collision event function. One parameter is an Entity pointer for the entity the player has collided with. This needs to be an Entity class so it can support collisions with all entity types. Now, I want a function to add an item to the player''s inventory. It takes an ItemEntity pointer. My problem is, you can''t pass an Entity class (from the collision function) to this function, because it''s the base class and doesn''t have the item stuff. Have I got this object orientation wrong, is there a better, but still elegant way of doing this? I don''t really want to abandon the inheritance thing in favour of some entitytype enum or something. Thanks, Frank
Wilka
Wilka
If you want to keep your existing class setup, there's a couple of ways you can do this. The first is to add a virtual IsItem function to the entity class. By default it should return false, and in your ItemEntity class make it return true. So you can ask the entity if it's an item, and if it is you cast it to what you want. To cast something, you can use static_cast. static_cast wont do any checking at runtime, so you'll need to make sure the thing your casting really is what you think (hence the IsItem function). So you'd have something like:


Player::Collide(Entity* pEnt)
{
if(pEnt->IsItem())
{
// static_cast<ThingYourCastingTo>(ThingYourCastingFrom)
ItemEntity* pItem = static_cast<ItemEntity*>(pEnt);
AddToInvertory(pItem);
}

// rest of function
}


Another way is to use the RTTI (Run Time Type Info) of C++, doing it this way means you don't need to have a function to ask a class if it's over a certain type. Instead of static_cast, you use dynamic_cast which will return NULL if pEnt isn't really an ItemEntity*. If it is, it'll return ItemEntity* to pEnt. So the above code would become:


Player::Collide(Entity* pEnt)
{
if(ItemEntity* pItem = dynamic_cast<ItemEntity*>(pEnt))
{
AddToInvertory(pItem);
}

// rest of function
}


Edited by - Wilka on September 16, 2000 3:40:28 PM
Xai
Xai
Ok frankd ... here is the key. YOU CAN pass an Entity from the collision function into the add item function ... IF THE ENTITY IS AN ITEM. Do you see what I''m saying ... the list of entities are all pointed to by Entity pointers .. the abstract base class .. but the objects themselves are derived classes such as Items, Players, Bullets etc. So, when the player colides with an item, you pass that variable (the Entity pointer he colided with) to a function which will decide what to do with it (this may be what your colide function IS, I was thinking you meant it DETECTED the collision, but maybe you mean it is the handler) .. so anyway, your in the handler of the collision function and you have an entity pointer, so now you want to act on it based on it''s type ... For this ... use the second method Wilka posted ... the dynamic_cast operator.

It is important to note there are TWO (builtin) ways of doing this in C++ with different behaviors. If you are acting on the object differently for nearly all derived classes, or are looking for and exact match to one or more derived classes (exact match means if an Item is acceptable that does NOT make a SpecialItem automatcially acceptable) for this system you use RTTI and the typeid() function. like this.


type_info info = typeid(*entity);


then you use a series of if statements like this:


if(info == typeid(Player))
// do stuff
else if(info == typeid(Item))
// add item
else if( ... )
//
else
// unknown entity type, possibly an error
// or simply ignore it cause you can''t handle it


now .. for this particular use ... you want to check a relitively FEW number of cases ... and you DO want to automatically treat derived types the same ... so you would use this instead:


Item *item = dynamic_cast{Item*}(entity);
// replace {} with angle brackets .. sorry

if(item != NULL)
{
// add item here
}


Now .. i definately do not want to bash Wilka, because he answered your question correctly, and was helpful, BUT I must emphasis that his first sugestion is a very bad one (even though his second was the exact right one). DO NOT add members functions such as IsItem() to your base class interface, for first off you will see that you have to add a new function, and hence rebuild the ENTIRE tree of classes, every time you derive a new class that needs it ... now there IS a system you can use in which the base class provides ONE function ... such as GetType ... or the much more advanced version IsDerivativeType(TypeID), in which each type registers intself with the base type AT RUN TIME (this is the exact same technique documented in the article "Why Plugable Factories Rock My Multiplayer World") that would do the trick. Just be aware that to have the interface work, it needs to stay general, and unbloated.

Good Luck ... and sorry Wilka, I still believe that you know your shit ... just had to keep the amn from going the wrong route

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.