Having enemies shoot allies... or not

Hey there,
I was wondering how to have enemy bowmen accidentally damage allies when these come across the arrow path.

The intended behavior is this one:

  • If a projectile (whatever its kind is) hurts something that does not have a health component (let’s say a wall or an obstacle) it must be destroyed
  • If a non homing projectile hurts something that has a health component, it must inflict damage to the hit target and gets destroyed
  • If a homing projectile hurts something that has a health component but that this “something” is not the intended target, it goes through and keeps seeking its target

I am coming with the following modifications (in Projectile class):
What do you think of the solution? Any other suggestion?

   private void OnTriggerEnter(Collider other)
   {
       Health hitTarget = other.GetComponent<Health>();
       if (hitTarget == this.damageDealer) return; //to avoid shooting yourself
       //Ajout du comportement suivant: si homingMissile, on n'enlève les points de vie que de la cible
       if(hitTarget != null)
       {
           if(isHoming && target != hitTarget) return;
           hitTarget.TakeDamage(weaponDamage + projectileDamage);
       }
       Destroy(gameObject);
   }

PS: thank you @Brian_Trotter for suggesting that the damageDealer can hurt himself :upside_down_face:

This code is close, but if I’m standing in between an enemy and a homing bullet, I won’t take any damage or have any other effect on the bullet. 100 Secret Service Agents just realized they can’t do their job properly…

This can be fixed quite simply by reversing the order of the statements within the if(hitTarget!=null) method, unless this behaviour is what you’re looking for.

I’m going to take this a step further, as I thought about our Secret Service agents (and also thought about how real friendly fire works)…

Here is a version I might use for this concept, which takes the size fo the offending collider in mind to slow down the projectile and reduce it’s damage… It probably should be extended to include wall penetration as well…

        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject == instigator) return; //Avoid self harm
            if (other.TryGetComponent(out Health health)) //Anything within this block, Health is guaranteed valid 
            {
                if (health.IsDead()) return; //You could consider impacts here anyways, but capsules stay upright, so
                                             //It would capture hits up to a meter and a half over a dead body... no bueno
                health.TakeDamage(instigator, damage);
                SpawnEffectsAndInvokeHit(); //Not sure if you're at this point, but this just invokes the Hit event and spawns effects
                //Get bounds of the collider that we've hit.
                float energyAbsorbed = other.bounds.extents.sqrMagnitude;                
                //Given a standard collider with a radius of .5f and a height of 2, this will be 1.5f
                //Given a collider on a big dragon witha radius of 5 and a height of 5, this will be 75f 
                
                //Reduce speed by energyAbsorbed
                float newSpeed = Mathf.Max(0,speed - energyAbsorbed);
                //Reduce damage by the same ratio we removed speed.  
                damage *= newSpeed / speed;                                            
                speed = newSpeed;
                //If either is still functional, continue on our merry way regardless of homing
                if (speed > 0 && damage > 0) return;
            }
            //May be covered in later lectures
            foreach (GameObject toDestroy in destroyOnHit)
            {
                Destroy(toDestroy);
            }
            Destroy(gameObject, lifeAfterImpact);
        }

        private void SpawnEffectsAndInvokeHit()
        {
            onHit.Invoke();
            if (hitEffect != null)
            {
                Instantiate(hitEffect, GetAimLocation(), transform.rotation);
            }
        }

Actually… it only took me a moment to factor in the potential to shoot through walls (with the inherent effect of slowing down the projectile IF it has enough speed

        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject == instigator) return; //Avoid self harm
            if (other.TryGetComponent(out Health health)) //Anything within this block, Health is guaranteed valid 
            {
                if (health.IsDead()) return; //You could consider impacts here anyways, but capsules stay upright, so
                //It would capture hits up to a meter and a half over a dead body... no bueno
                health.TakeDamage(instigator, damage);
            }

            SpawnEffectsAndInvokeHit(); //Not sure if you're at this point, but this just invokes the Hit event and spawns effects
            //Get bounds of the collider that we've hit.
            float energyAbsorbed = other.bounds.extents.sqrMagnitude;                
            //Given a standard collider with a radius of .5f and a height of 2, this will be 1.5f
            //Given a collider on a big dragon witha radius of 5 and a height of 5, this will be 75f 
                
            //Reduce speed by energyAbsorbed
            float newSpeed = Mathf.Max(0,speed - energyAbsorbed);
            //Reduce damage by the same ratio we removed speed.  
            damage *= newSpeed / speed;                                            
            speed = newSpeed;
            //If either is still functional, continue on our merry way regardless of homing
            if (speed > 0 && damage > 0) return;
            
            //May be covered in later lectures
            foreach (GameObject toDestroy in destroyOnHit)
            {
                Destroy(toDestroy);
            }
            Destroy(gameObject, lifeAfterImpact);
        }

        private void SpawnEffectsAndInvokeHit()
        {
            onHit.Invoke();
            if (hitEffect != null)
            {
                Instantiate(hitEffect, GetAimLocation(), transform.rotation);
            }
        }

With this, if anything besides the player is hit, first we test for Health, and damage accordingly.
Next, we check the sqrMagnitude of the collider that was hit, and reduce the projectiles speed accordingly. If we have effects to spawn at this point, we do so.

Now if the speed or damage has been reduced to zero, we set the object up for destruction.

If you hit a health, the first hit will do full damage, but the second target hit will do less damage, and will reduce in speed. This is very much like how real world projectiles would occur, if I jumped in front of a bullet to save you, the bullet might still travel right through me, but it won’t hit you as hard.

If you hit a wall, that projectile might go through if it’s a thin wall, or it might stop cold if it hits a 10x10x10 building.

There are other factors that could go into play for a system like this…
You could, for example, have a “piercing” value that modifies how much speed is lost… it could be scaled by the shooter’s offense or the target’s defense (with non-health items having the most piercing resistance).

Wow! :open_mouth:
Really impressive… Thanks a lot: wasn’t expecting such an elaborated solution…
Great idea! I will definitely go on with that solution.
Masterclass :sunglasses:

Privacy & Terms