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!

Adding UI to a VR Game

The Problem with UI in VR

Building a user interface on a flat screen is relatively straightforward. Pick a position on the screen where you want your UI to show and it shows there. As long as you don't make it too intrusive, you can just let that UI hang out there, on top of everything else going on in your game. That is not the case for VR. Here is why.

First, in VR, having a UI element consistently stuck in the same place as the player is navigating the VR world can be a jarring experience. At best, it breaks the immersion and reminds them they are seeing this world through a screen. At worst, it can cause confusion and VR sickness as people try to make sense of the strange behavior and visual stimuli they are seeing. Thus, you need the UI to "belong" in that VR world.

Second, having a UI element always take part of your field of view reduces how much of that world you can see, which is just a shame. Therefore, you need to make the UI easy to find and easy to remove from view.

Make the UI belong in the world

Getting the UI to fit your experience can mean many things. In the simplest of terms, it needs to feel like a real object in the world. There are a couple of ways to achieve this. One way is to make it a static object. Think of it as placing and actual screen inside your world and displaying your UI there. This tends to be very useful for contextual UI. For example, if you are controlling a large spaceship, you may have several stations for controlling different systems and you may need to virtually move in that world to access all of the controls as they are mapped to virtual stations. Another way to make your UI fit the virtual world is to make it follow the player. Mind that following the player is different from being stuck to the player's view. Often, the most intuitive UI follows both principles. Think of a "wearable" UI. For example, if you let the player have a virtual wristband that displays relevant information then the player can glance at their wrist when they need that information but will otherwise not obstruct their view.

In my case, I went with UI that follows the player. Initially, I had the score and some debug text simply stuck to the player position. It was not that intrusive since the player could look around and the UI would not follow their gaze, it would stick to the player position in the world. It had the issue that if the player turned around the UI would stay behind them, out of view. It was still better than having the UI stuck to the player's view but it could be better. So I made it a bit better. I added a script to the UI canvas so that it would constantly look at the camera and had it follow a point in front of the player so that it would hang out in front of them if the player was still and the UI would follow the player around when they moved.

void FollowCamera()
    {
        transform.LookAt(lookAt, Vector3.up);  // lookAt is the camera transform
        transform.Rotate(0f, 180f, 0f);  // Rotate the canvas 180 degrees on the Y so the canvas faces the camera
        Vector3 newPosition = transform.position;
        // use Lerp (linear interpolation) to get the canvas to approach the followPosition transform
        // (the point in front of the player). followSpeed determines how fast the canvas closes the distance
        newPosition.x = Mathf.Lerp(transform.position.x, followPosition.position.x, followSpeed * Time.deltaTime);
        newPosition.y = Mathf.Lerp(transform.position.y, followPosition.position.y, followSpeed * Time.deltaTime);
        newPosition.z = Mathf.Lerp(transform.position.z, followPosition.position.z, followSpeed * Time.deltaTime);
        transform.position = newPosition;
    }

Make the UI easy to find and easy to remove from view

I have yet to do a good job at this. In part because I think that, in my case, I want the player to be constantly focused on how their actions affects their score so I have the score always visible. Even though it is not the most intrusive piece of UI, it still could be made easier to hide. For example, making it hang around knee-high level would make it easier to keep the UI out of view when the player is looking straight ahead but will make it a cinch to find the score by simply looking down a bit.

Object Pooling

Object Pooling

Soon after the first couple of levels were done, it became very obvious that there were some performance issues. One obvious optimization to make was pooling the projectiles instead of constantly instantiating and destroying them. Setting the pool up was easy enough; set up a pool singleton that is generic enough to take any kind of gameobject (this would come in handy later) and building some methods to take objects in and out of the pool. There were some bugs around properly activating and deactivating objects but otherwise it was up and running very quickly.

Once I had the projectiles pooling, I added another pool to handle the audio source, both for the firing sound and the collision impact sound. In later iterations, I added a separate pool for the different VFX required, such as muzzle flash, impact effects and so on. The VFX pool is actually handled by a pool object from a VFX asset package. The Asset Store saves the day again (or at least saves me a ton of time).

Rigid Bodies and Momentum

There was an insidious bug that took a while to figure out. As soon as I started pooling the projectiles, I found that some of them wouldn’t fire properly. Some would be way too fast, some would fire in a weird and unexpected angle. With some poking, I figured out it had to be the momentum of the projectile transferring from the projectile’s last state to the initialization coming out of the pool. For quite a while, I tried fixing this at deactivation time. The projectiles are pooled once they hit a surface so that seemed like the best place to kill the momentum and reset the projectile. At this point, I’m not 100% sure what the problem was but the projectiles were either being pooled before they were fully reset or the firing script was getting them out of the pool before the reset took hold. In either case, the solution was staring at me in the face, resetting the projectiles during the instantiation method. Instead of trying to return them to the pool with no momentum, it was better to kill the momentum right before adding the “firing” momentum. Once I reset the momentum right before firing the projectile (within the firing method), the weird projectile angles went away.

Other progress

By this time, the game has a couple of levels, a few materials, including some translucent glass. The game also has a few sound effects and some background music, too. The text is fixed to the player position and the score has a nicer text style. There’s some fun debug text telling you what the state of the game is. Eventually, that text will be replaced with more impactful messaging but this gets the state of the game across for now (just so that people don’t get lost at the end of the level and such).