Sign in to follow this  
Mr_Threepwood

C++ "Is a" question

Recommended Posts

Mr_Threepwood    150
I've got a function that is called whenever two objects of type Sprite collide, sprite has several derived classes, and I'm unsure how to properly do an "is a" type of call or something on the objects. What I mean is that if the function is called and one sprite is a player bullet, and one is a monster then I want to call the TakeDamage() function on the monster. But when the method is called all I get is two sprite pointers so I need to do the check. Currently I've got it working by just checking the sprites bitmap and seeing if it is a monster, but to me that seems like a poor solution. Is there some way I can either check the class type of an object? Another solution I was thinking would be to have some constant in the base sprite class that has to be set in all derived classes or something (a virtual constant or something, does that exist?). I was tempted to just make it a member variable of the sprite class and in the constructor of all the derived classes set it, but that seems like a poor solution to me. So how would I do this properly? (this is all C++ btw). Thanks in advance.

Share this post


Link to post
Share on other sites
SiCrane    11839
In C++, you can determine the dynamic type of an object with virtual functions by using the typeid() operator. Let's say you have an pointer to an Object ptr. typeid(*ptr) will give you a std::type_info object that contains its dynamic type. You can also cast the pointer with dynamic_cast to see if it's derived from a given type. Ex: Frog * frog = dynamic_cast<Frog *>(ptr) will be non null iff ptr points to a Frog or something derived from a Frog.

However, the term for what you are trying to do is "double dispatch". I suggest doing a web search for "C++ double dispatch".

Share this post


Link to post
Share on other sites
Ezbez    1164
There are numerous ways to do this. One way could be to give Sprites a virtual function called HitByABullet(Bullet& b); or similar. Then, when the bullet's collision-response is called, it can call HitByABullet() on the thing it collided with. Since the function is virtual, each base-class can do as it pleases; play a 'ping' sound, loose health, deflect it, whatever you want.

This assumes that Sprite already has a virtual collision-response function that is called on both objects when a collision occurs.

Edit: ...and all this is just "double-dispatch" but tailored to your example.

Share this post


Link to post
Share on other sites
Mr_Threepwood    150
Ok thanks for the replies, but I'm still a bit confused. So here's what I have get called when any two sprites collide (bullets are represented by sprites in my program):


BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)
{
Bitmap* hitterBitmap = pSpriteHitter -> GetBitmap();
Bitmap* hitteeBitmap = pSpriteHittee -> GetBitmap();
int gunDamage = g_pMainCharacter->GetGunDamage();

if ( (hitterBitmap == g_pBulletBitmap) && (hitteeBitmap == g_pMonsterBulletBitmap) )
{
pSpriteHittee->TakeDamage(g_pMainCharacter->GetGunDamage());
pSpriteHitter->TakeDamage(g_pMainCharacter->GetGunDamage());


}
else if ( (hitterBitmap == g_pBulletBitmap) && (hitteeBitmap == g_pMonster2Bitmap) )
{

pSpriteHittee->TakeDamage(g_pMainCharacter->GetGunDamage());
pSpriteHitter->TakeDamage(g_pMainCharacter->GetGunDamage());

}



return FALSE;
}



So the biggest problem is the identifying what the sprites sent in are, so that I know whether or not to call the TakeDamage() function. If two monsters collide for example, nothing should happen.

So right now I've worked up a semi ugly way were I just check the bitmap of the sprite, to see if it's a bullet or not, and then if it is I call the TakeDamage method on both objects. Another bad thing is that I've got to call this on the gun bullet too, even though really the gun bullet isn't just taking damage, it's supposed to be destroyed. This works now because I set all bullets to have 1 hp which is the minimum amount of damage that a bullet can do.

So would the best way to identify a bullet be to try to cast it to a bullet, and if that fails then it's not a bullet?

Share this post


Link to post
Share on other sites
rip-off    10979
C++ is set up that only an object instance knows its dynamic type. Calling a function based on the dynamic type of a single object is easy - good old virtual functions are the answer. Calling functions based on the dynamic type of two objects is not supported directly in the language. This is essentially what you want to do, you have two objects of various types, and you want to decide which code to run based on the both types. We can simulate it in C++.

I recommend you re-read the wikipedia article linked by Gage64. It has example C++ code which illustrates dynamic dispatch - with a most appropriate example (game object collision handling). [smile]

If you have any questions on the technique described there, post them here.

Share this post


Link to post
Share on other sites
Mr_Threepwood    150
Ok I did read it two times, but I think the third time round gave me a better idea. So I'd do something like this:

have a virtual function called CollideWith(Sprite* hittingSprite) in the Sprite class. The implementation in the actual sprite class is set up to do nothing.

Then in the various subclasses, I put their own implementation of CollideWith that specifies different sprite types. So like in Monster (the class that all monsters are derived from), I'd have:

void CollideWith(Bullet* bulletToCollide)
{
TakeDamage (bulletToCollide->GetDamage())
}

and in Bullet I'd need to overload the CollideWith function to do something like:

void CollideWith(Sprite* spriteHit)
{
spriteHit->CollideWith(this)

//Kills the bullet sprite
kill()

}

In order to overcome the situation described in the article where you get the base classes method called instead of the derived version. Right?

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by Telastyn
And of course a Bullet is not a Sprite. A sprite might represent a bullet, but...


Indeed. You should have classes for the player and monster which contain Sprite objects, and sprites that are not polymorphic: there is only one kind of sprite - it's a wrapper for a bitmap and some data about what parts of the bitmap are relevant, and its size.

When a sprite collision is detected, you tell the containing objects about it, and *they* do double dispatch work. Or rather, the containing objects initiate a collision detection, and if it happens, they send the appropriate messages. If a double dispatch is needed, they do it.

An elaborate example:


// BTW, C++ has a perfectly good boolean type with real values and type safety.
// Why not use it? :)
bool spritesCollide(Sprite* x, Sprite* y) {
// Just check if the rectangles intersect!
}

void Entity::checkCollision(Entity* e) {
if (spritesCollide(sprite, e->sprite)) {
hit(e); // virtual function
e->hit(this);
}
}

void Bullet::hit(Entity* e) {
e->takeDamage(size);
markSelfDestroyed();
}

void Monster::hit(Entity* e) {
e->hitByMonster(this);
}

void Player::hit(Entity* e) {
e->hitByPlayer(this);
}

// Default collisions do nothing.
virtual void Entity::takeDamage(int x) {}
virtual void Entity::hitByPlayer(Player* p) {}
virtual void Entity::hitByMonster(Monster* m) {}

// Bullets aren't affected by anything.
// Monsters aren't affected by other monsters, but they are affected by players.
void Monster::hitByPlayer(Player* p) {
p->attack(this);
}

void Monster::takeDamage(int x) {
// take damage
}

// Players aren't affected by other players, but they are affected by monsters.
void Player::hitByMonster(Monster* m) {
m->attack(this);
}

void Player::takeDamage(int x) {
// take damage
}

void Player::attack(Monster* m) {
m->takeDamage(someFunkyCalculation());
}

void Monster::attack(Player* p) {
p->takeDamage(someOtherFunkyCalculation());
}



Notice how we don't just attack() collided-with sprites directly: this (admittedly complicated) process lets us negotiate to avoid "friendly fire".

Share this post


Link to post
Share on other sites
Mr_Threepwood    150
Oh ok that makes a lot more sense Zalhman. I've just recently read "Beginning Game Programming" by Michael Morrison, and in his examples he always uses Sprite as the derived class, but it does make more sense that the sprites are owned by other objects since all they are supposed to be is visual representations that can be used for collision detection. The reason he didn't was probably to not confuse beginners like me by adding a new way of arranging things towards the end of the book.

At this point I'd say I'm too far in my test game to switch everything to a more proper structure, since with my C++ skills if I re-do things it usually means redo for x amount of time, debug all the errors cause as a result for x*5 amount of time. I did redo parts of the engine he gives in the book to make the game better (his coordinate system was all in ints, and I changed everything to floats so that you could have non-int velocities), but debugging that took forever. Game dev is a hell of a lot of learning so far, and I've barely scraped the surface, but it's awesome.

Edit:

As for the bool thing, I know that normal bool's exist, but for some reason in the book type "BOOL" is always used, and I guess he did it because some of the windows functions needed to have type BOOL and not bool, and he just decided to be consistent by using BOOL.

Share this post


Link to post
Share on other sites
Mr_Threepwood    150
Ok so I'm having some difficulties putting this into action, the problem is that I can't get it to call the right version of the functions.

So I've changed my function that receives the two sprites (whose derived class is unknown) to this:


BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)
{

pSpriteHitter->CollideWith(pSpriteHittee);
pSpriteHittee->CollideWith(pSpriteHitter);

return FALSE;
}



Not sure if I should do the CollideWith on both or just one, but that's not the issue for now.

So my Sprite structure is:

Sprite
-Monster
----HulkMonster
-Player
-Bullet

For now I'd be really happy if I could just get a Bullet-HulkMonster collision working properly.

In the sprite class I added:


void Sprite::CollideWith(Sprite* spriteToHit)
{
}



I figured that would be ok since I don't want anything to happen with some sprite interactions, so doing nothing could be the base function.

Now in bullet I did:

void Bullet::CollideWith(Sprite* spriteToHit)
{
spriteToHit->CollideWith(this);
Kill();
}




Hoping to call the CollideWith(Bullet* theBullet) function on whatever sprite it collides with. So I also put a CollideWith(Bullet* theBullet) in the HulkMonster class.

However, the CollideWith(Bullet* theBullet) function is never getting called. I traced it through and when I call the spriteToHit->CollideWith(this); from the Bullet class, it's just calling the regular CollideWith(Sprite* spriteToHit) rather than the bullet class. Is there something I'm doing wrong?

I also noticed that the "this" keyword sends it as a constant or something, so I'm not sure if that means I'm supposed to change my CollideWith function that takes a bullet to conform with this? I tried several different ways and traced things through but couldn't ever get it to work.

Another foreseeable problem is that wouldn't calling the spriteToHit->CollideWith(this); fail for every class that doesn't define a Bullet collision? or is the idea that it sees this and performs just a sprite collision which has been defined in the base Sprite class?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this