Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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!

share|improve this question
add comment

1 Answer

up vote 2 down vote accepted

You could simplify the checks by calculating the squared distance from the center of the explosion to each corner, then comparing the minimum squared value to the square of the radius to see if there is an overlap. If so, then calculate the square root of the minimum distance to calculate the percentage of damage. However, your code looks efficient enough, so I'd recommend profiling both options to see which is better. What you have is well documented and readable and I don't think the performance needs optimization.

The reason for the occasional negative values for damage is the order of operations. You are performing this calculation:

(distanceFromExplosionCenter/explosionRadius) * explosionMaxDamage

before subtracting the result from 1. If the result of that multiplication is greater than 1, you'll get a negative damage value.

Change your existing code to:

damageToApply = (1 - (distanceFromExplosionCenter/explosionRadius)) * explosionMaxDamage;

This will calculate the percentage first, then multiply by maximum damage so that you don't end up with negative values.

share|improve this answer
    
Good catch with the parenthesis. Changed it and it works just fine. I will try your suggestion tonight and compare the results/efficiency. –  Paul Renton Jul 3 '13 at 14:21
    
Accepted.. Would have liked to see some alternatives for calculating damageToApply. The catch on the parenthesis saved me many a headache. –  Paul Renton Jul 9 '13 at 1:53
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.