Limiting damage with multiple colliders

Hi folks,

I’m working on a top-down shooter thing with a starfish for a main character and have got him set up with a compound collider consisting of a box collider on each arm. This works well about 90% of the time, however every now and then he is at just the right angle so that an enemy running into him will hit two arms at the same time. This causes double the normal amount of damage.

Currently, each collider has a script that uses SendMessageUpwards to tell the character’s ApplyDamage function to subtract health. What I’m looking for is a way to tell the ApplyDamage function to ignore messages from additional colliders and only apply the normal amount of damage no matter how many arms have been hit. Different enemies cause different amounts of damage, so I can’t easily restrict that amount in the ApplyDamage script.

Thanks.

The first thing that comes to mind is to enforce that the same enemy can only apply damage once in a set period of time (where the period of time is something fairly short, like .25 seconds).

Implementing this would probably involve storing a container of ‘recent damage’ entries specifying what enemy caused the damage, and when the damage occurred. Whenever damage was to be applied, you would check the container to see if there’s an entry for that enemy; if so, the damage would not be applied. Every game update you would also remove from the container any entries that had expired (e.g. had been there longer than the ‘buffer period’).

This should fix the problem of an enemy running into two or more arms more or less simultaneously. There could be other issues that might arise as a result of applying this method though, so it might require some tweaking to get the desired results.

Anyway, that’s what first comes to mind, but maybe others will be able to offer different suggestions.

Thanks Jesse, I appreciate the reply. The problem is the double damage/multiple collider contact occurs in the same frame and so setting up a container like that wouldn’t take effect until the next frame, right?

The enemies are destroyed once they run into the main character, so it’s just the double damage within one frame that I’m worried about.

Hmmm.

What about putting the collision detection script on the side where there is only one gameObject (the enemy) ?

It should still work. The first time the ‘on collision’ callback is called, the damage is applied, and the enemy is added to the ‘recent damage’ list. The next time the ‘on collision’ callback is called (even if it’s within the same frame), the enemy is found in the ‘recent damage’ list, and damage is not applied.

In fact, the fact that the enemies are destroyed when they run into the player makes it even easier, as you can dispense with the time stamp and just maintain a container of enemy references that’s cleared every update.

The only potential issue I can think of here is that when considering events that occur within a single game update, the order in which Unity does things ‘under the hood’ comes into play. I’d be willing to bet that if you clear out the ‘recent damage’ enemy container in the FixedUpdate() function, everything would work as expected. (This is based on the assumption that collision callbacks only occur when the physics system is updated, which only happens during fixed updates.) Or, if you wanted to be safe, you could clear the container in Update().

To summarize the method I’m suggesting, in your ‘player’ class/script you would have a list of enemy references, e.g. (pseudocode):

list<Enemy> recentDamageList;

In your Update() or FixedUpdate() function, you clear this list (again, this is just pseudocode):

recentDamageList.clear();

Your ‘apply damage’ function would then look something like this:

void ApplyDamage(Enemy enemy)
{
    if (!recentDamageList.Find(enemy)) {
        recentDamageList.Add(enemy);
        enemy.Destroy();
        health -= enemy.damage;
    }
}

I can’t guarantee this solution 100%, but I’m pretty sure it’s the right idea.

Actually, here’s one more suggestion, which actually might be easier to implement:

I’m guessing that when you destroy a game object in Unity, it’s not actually removed until the end of the update; as such, removing a game object doesn’t prevent it from interacting with other objects and generating additional collisions within the same update (as you’re discovering).

However, you could simply add a ‘flagged for removal’ variable to the enemy class, and set it to true when a collision occurs. Your code would now look something like this:

void ApplyDamage(Enemy enemy)
{
    if (!enemy.flaggedForRemoval) {
        enemy.Destroy();
        enemy.flaggedForRemoval = true;
        health -= enemy.damage;
    }
}

This would most likely be both more efficient, and easier to code.

I don’t think that would help, as there would still be multiple collisions (and therefore multiple invocations of the ‘apply damage’ function).

The idea of a boolean tag “must be destroyed” is pretty good. Upon collision, it checks whether it has already triggered once or not, and set it to true if not done yet.

what about resizing the coliders like make 1 colider a little bit longer than the other ones so it can’t happen

How would that keep it from happening? Just because one collider was a little longer than the other wouldn’t mean that another object couldn’t hit both of them at the same time.

Thanks very much for all the ideas guys. I ended up using a variation of Jesse’s flagged for removal suggestion so that the first collision triggers a boolean and activates the apply damage function. It seems to work pretty well so far. Thanks again.