Designing for a chaining projectile (2D)

Hey all,

Still a bit new to the Unity scene, but got some programming background.

My current short-term goal is to produce a script interaction that allows X number of chains on a projectile, naturally decrementing each time it hits an enemy.

As of right now my strategy is using a OnTriggerEnter2D function on the projectile itself (passing X as number of chains on a constructor). So when the projectile collides with an object tagged as ‘Enemy’ (later possibly ‘Terrain’ and such) it will trigger logic to decrement the chain count and find+attack the nearest enemy, repeating the process.

ala

void OnTriggerEnter2D (Collider2D col) {

        if (col.tag == "Enemy"){
          EnemyHealth Healthinter = col.GetComponent<EnemyHealth>();
          //This check can be used later to differ behaviours between enemies/terrain/other
          if (Healthinter != null) {
            killed = Healthinter.damageReceived(damage);
          }
        }
        chain--;
        // logic for finding nearest enemy
    }

This code does what I need it to so far, but here’s where my design indecision is coming into play.

  • Would it be better to send the current projectile with a decremented count? Or maybe destroy the current projectile after creating a new projectile?
  • Would it be better to use a global ‘FindObjectWithTag(Enemy)’ or is there a better way to track enemy proximity to a specific projectile object? (Maybe secondary OnTriggerStay with a set range?)
  • Is OnTriggerEnter the right tool for this, or is there some other more advanced strategy that’s better for performance and/or sanity I’m missing? Being projectiles, there will be a lot of these entities and I don’t feel a col.GetComponent() check per projectile scales well, but I could be wrong.

I can’t shake the feeling that there’s some significantly easier way to do this or at the very least to do the second half of this and I am confident this isn’t a rare scenario for many games (chain lightning and the like).

Apologies if this is a common question.

What exactly do you mean by “chains?” Do you mean how many times this bullet can hit enemies during its lifetime? If so then I suppose your approach would be just fine… but a little odd because the bullet will continue THROUGH the entire trigger zone doing nothing else, since it would only impact at the “Enter” point.

Destoying the curent projectile would be wasteful of the memory used to keep track of it, definitely don’t do that. reuse the projectile where possible.

What I would probably do in this instance to find the next enemy is use Physics2D.OverlapCircleAll() to get the enemies with a certain radius of the player, then select randomly from that list, set the new target as the next destination, and so-on. OnTriggerEnter would be the only proper time to use this in the way you’ve described. using OnTriggerStay will happen as long as the projectile is in an object so it will fire multiple times as its travelling though so that’s probably not what you want.

as for using GetComponent, its probably the only reliable way to do this… at least that I can think of

Chaining meaning when the bullet hits Enemy 1, it then gets thrown at Enemy 2, then Enemy 3, etc etc. Other synonymous mechanics might be Ricochets or similar. An easy example would be a Chain Lightning Spell (granted that’d be a ray not a projectile but same principle), when the Enemy 1 is hit the lightning needs to chain to Enemy 2 in range and then Enemy 3 and so on.

it’s a bit tricky to encompass everything needed because you need the projectile in question to:
A) Keep track of the number of times it’s chained
B) Stop chaining when it reaches its max
C) Exclude the target it just hit so it doesn’t just hit the same enemy X times.
D) Ideally (stretch goal) limit the chaining range and angle to avoid weird edge cases

Cool, I’ll give that function a look, thanks! Yeah you’re probably right on the OnTriggerStay, I’m thinking it’ll be mostly on getting the actual collider area of both to play and look nice.

Ah gotcha, that makes sense.

You would want to find the nearest enemy, one that this bullet has NOT hit before, as you note above. Could just put a reference to him in a collection on the bullet and check that collection at retargeting time.

ALSO… you might want to have the bullet die if it has travelled beyond a certain distance too, not just times hit, just in case the enemies are spread out, that one bullet might live a long time if it has a long way to go.

Another approach might be to find ALL the enemies you plan to hit when the first hit happens, then just play them down one after the other from the found list, so if others join the group while the bullet is going, it won’t hit them… or maybe you want that!