Determining explosion radius damage - Circle to Rectangle 2D

Posted by Paul Renton on Game Development See other posts from Game Development or by Paul Renton
Published on 2013-07-02T15:28:34Z Indexed on 2013/07/02 17:16 UTC
Read the original article Hit count: 345

Filed under:
|
|
|

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!

© Game Development or respective owner

Related posts about math

Related posts about Performance