Explosive Reactions

Adding some oomph

The game being a physics based puzzle shooter, having some elements with strong physics influence (i.e. strong forces) is a fast way to add more power to the interactions. Sure, the gun itself can cause plenty of damage to the environment but there are moments when you want to add a higher point. Enter explosions. Explosions are a simple way to add a ton of force to the scene without needing to address the multiple projectiles or varied ammunition problems. In a nutshell, explosions are straightforward. Select a radius of influence and select the amount of force to add within that radius. The physics engine takes care of the rest.

public void Explode()
{
    Vector3 explosionPos = transform.position;
    Collider[] colliders = Physics.OverlapSphere(explosionPos, radius);
    foreach (Collider hit in colliders)
    {
        Rigidbody rb = hit.GetComponent<Rigidbody>();

        if (rb != null)
        {
            rb.AddExplosionForce(power, explosionPos, radius, upwardsModifier);
        }
    }
    Destroy(gameObject);  // Bye, bye barrel
}

The trick is to use Physics.OverlapSphere() to get all the colliders that intersect the sphere volume and then iterate over those colliders applying an explosion force (RigidBody.AddExplosionForce()) at the origin of the explosion so that the rigid bodies are affected properly.

Fire begets fire and other chain reactions

From there, there are a few things to consider. If you have two explosives next to each other, does one trigger the other? If so, you need to set up a way to do that. In my case, I had a <Barrel> script that adds all the explosive behavior above so checking if the colliders involved in the explosion belonged to a game object that itself had a <Barrel> component served as the qualifying condition. If that condition was met, I could trigger an explosion on that game object as well, thus having one barrel exploding trigger the other.

One problem with that approach was the feedback loop it created. If one barrel triggers another while exploding, the latter could trigger the former again inside its exploding logic and round and round we go until we hit the recursion limit and blow up the stack instead of the barrels. There are many ways to solve that but I approached it by having a hasExploded boolean that was set to true before triggering the explosions in other elements so that those elements could not trigger the exploding logic again. The way I went about capturing the barrels to trigger was to collect them in a List so I could make them explode later.

public void Explode()
{
    if (!_hasExploded)
    {
        Vector3 explosionPos = transform.position;
        Collider[] colliders = Physics.OverlapSphere(explosionPos, radius);
        foreach (Collider hit in colliders)
        {
            Barrel barrel = hit.GetComponent<Barrel>();
            Rigidbody rb = hit.GetComponent<Rigidbody>();

            if (barrel != null)
            {
                _chainReactors.Add(barrel);
            }
            if (rb != null)
            {
                rb.AddExplosionForce(power, explosionPos, radius, upwardsModifier);
            }
        }

        _hasExploded = true;  // set flag to true to prevent other barrels from triggering this one

        foreach (Barrel barrel in _chainReactors)
        {
            barrel.Explode();
        }
    }

    Destroy(gameObject); // Bye, bye barrel
}

Rounding things up with some polish

By now, we have nice exploding barrels. They make other barrels explode. Amazing. They look off, though. For starters, there's no explosion visual effect so it is hard to tell that an explosion took place. As with other VFX in this project, we will get an explosion particle effect from the explosions pool and put it in the right place, where the barrel is sitting. Much better.

Things are a little quiet though. We should add an explosion sound effect. Much like the VFX, the SFX is also pulled from an explosion SFX pool and placed in the right position, right where the barrel is sitting.

Now the picture is complete. Or so you would think. In practice, there is one problem. All the barrels triggered by the chain reaction explode, basically, on the same frame. Pretty cool for massive explosions but rather lame as a game mechanic. What if the barrels had a bit of a fuse to them? What if they took that extra fraction of a second to explode? The result, a fun explosion chain (or is it a chain of explosive fun?) that has the barrels first be blown away by the explosion force and only then have them explode themselves. This turns out to be also an interesting mechanic where you can use one explosion to push another barrel into the right location and have the barrel explode there. Can make for some interesting puzzles.

In the video you can see the final product and a couple of easy puzzle levels to use the exploding mechanic and, particularly, the chain reaction of explosions.

public void Explode()
{
    if (!_hasExploded)
    {
        Vector3 explosionPos = transform.position;
        Collider[] colliders = Physics.OverlapSphere(explosionPos, radius);
        foreach (Collider hit in colliders)
        {
            Barrel barrel = hit.GetComponent<Barrel>();
            Rigidbody rb = hit.GetComponent<Rigidbody>();

            if (barrel != null)
            {
                _chainReactors.Add(barrel);
            }
            if (rb != null)
            {
                rb.AddExplosionForce(power, explosionPos, radius, upwardsModifier);
            }
        }

        // Explosion VFX
        GameObject explosion = CFX_SpawnSystem.GetNextObject(explosionPrefab);
        explosion.transform.SetPositionAndRotation(explosionSpawn.position, explosionSpawn.rotation);
           
        // Explosion SFX
        var explosionAudio = ObjectPool.Instance.GetExplosionAudio();
        explosionAudio.transform.SetPositionAndRotation(transform.position, transform.rotation);
        explosionAudio.GetComponent<ExplosionAudio>().Trigger();
            
        StartCoroutine(KillExplosion(explosion, explosionLife));

        _hasExploded = true;  // set flag to true to prevent other barrels from triggering this one

        foreach (Barrel barrel in _chainReactors)
        {
            barrel.DelayedExplosion();
        }
    }

    Destroy(gameObject); // Bye, bye barrel
}

public void DelayedExplosion()
{
    StartCoroutine(DelayExplosion(explosionDelay));
}

private IEnumerator DelayExplosion(float delay)
{
    yield return new WaitForSeconds(delay);
    Explode();
}

private IEnumerator KillExplosion(GameObject explosionInstance, float delay)
{
    yield return new WaitForSeconds(delay);
    explosionInstance.SetActive(false);
}

Have fun making your game objects explode!

Posted in VR Development and tagged , , .