Determining explosion radius damage - Circle to Rectangle 2D
- by Paul Renton
One of the Cocos2D games I am working on has circular explosion effects. These explosion effects need to deal a percentage of their set maximum damage to all game characters (represented by rectangular bounding boxes as the objects in question are tanks) within the explosion radius. So this boils down to circle to rectangle collision and how far away the circle's radius is from the closest rectangle edge. I took a stab at figuring this out last night, but I believe there may be a better way. In particular, I don't know the best way to determine what percentage of damage to apply based on the distance calculated.
Note : All tank objects have an anchor point of (0,0) so position is according to bottom left corner of bounding box. Explosion point is the center point of the circular explosion.
TankObject * tank = (TankObject*) gameSprite;
float distanceFromExplosionCenter;
// IMPORTANT :: All GameCharacter have an assumed (0,0) anchor
if (explosionPoint.x < tank.position.x) {
// Explosion to WEST of tank
if (explosionPoint.y <= tank.position.y) {
//Explosion SOUTHWEST
distanceFromExplosionCenter = ccpDistance(explosionPoint, tank.position);
} else if (explosionPoint.y >= (tank.position.y + tank.contentSize.height)) {
// Explosion NORTHWEST
distanceFromExplosionCenter = ccpDistance(explosionPoint,
ccp(tank.position.x, tank.position.y + tank.contentSize.height));
} else {
// Exp center's y is between bottom and top corner of rect
distanceFromExplosionCenter = tank.position.x - explosionPoint.x;
} // end if
} else if (explosionPoint.x > (tank.position.x + tank.contentSize.width)) {
// Explosion to EAST of tank
if (explosionPoint.y <= tank.position.y) {
//Explosion SOUTHEAST
distanceFromExplosionCenter = ccpDistance(explosionPoint,
ccp(tank.position.x + tank.contentSize.width,
tank.position.y));
} else if (explosionPoint.y >= (tank.position.y + tank.contentSize.height)) {
// Explosion NORTHEAST
distanceFromExplosionCenter = ccpDistance(explosionPoint,
ccp(tank.position.x + tank.contentSize.width,
tank.position.y + tank.contentSize.height));
} else {
// Exp center's y is between bottom and top corner of rect
distanceFromExplosionCenter = explosionPoint.x - (tank.position.x + tank.contentSize.width);
} // end if
} else {
// Tank is either north or south and is inbetween left and right corner of rect
if (explosionPoint.y < tank.position.y) {
// Explosion is South
distanceFromExplosionCenter = tank.position.y - explosionPoint.y;
} else {
// Explosion is North
distanceFromExplosionCenter = explosionPoint.y - (tank.position.y + tank.contentSize.height);
} // end if
} // end outer if
if (distanceFromExplosionCenter < explosionRadius) {
/*
Collision :: Smaller distance larger the damage
*/
int damageToApply;
if (self.directHit) {
damageToApply = self.explosionMaxDamage + self.directHitBonusDamage;
[tank takeDamageAndAdjustHealthBar:damageToApply];
CCLOG(@"Explsoion-> DIRECT HIT with total damage %d", damageToApply);
} else {
// TODO adjust this... turning out negative for some reason...
damageToApply = (1 - (distanceFromExplosionCenter/explosionRadius) * explosionMaxDamage);
[tank takeDamageAndAdjustHealthBar:damageToApply];
CCLOG(@"Explosion-> Non direct hit collision with tank");
CCLOG(@"Damage to apply is %d", damageToApply);
} // end if
} else {
CCLOG(@"Explosion-> Explosion distance is larger than explosion radius");
} // end if
} // end if
Questions:
1) Can this circle to rect collision algorithm be done better? Do I have too many checks?
2) How to calculate the percentage based damage? My current method generates negative numbers occasionally and I don't understand why (Maybe I need more sleep!). But, in my if statement, I ask if distance < explosion radius. When control goes through, distance/radius must be < 1 right? So 1 - that intermediate calculation should not be negative.
Appreciate any help/advice!