Shooting Gallery — Energy Barrier

Sean Duggan
3 min readJun 1, 2024

--

Another optional objective for the shooting gallery lesson I was following was to set up a barrier that can be shot to be deactivated. That seems like it would be easy enough, but first we have a bit of bookkeeping. In the last article, regarding explosive barrels, I mentioned that we were doing a lot of shooting logic inside of the FPS Controller in terms of what happens when we hit various surfaces. Instead, I’m going to move that to the objects themselves by creating an IShootable interface.

public interface IShootable {
public void HandleShot();
}

Basically, any class that I tag with this has to implement a HandleShot method. Coincidentally, the target class already has a method by that name, so implementing it there is as easy as adding IShootable to its inherited classes/interfaces.

public class AI : MonoBehaviour, IShootable

Similarly, I’m going to go to our barrel script, and add that modifier.

public class BarrelExplosion : MonoBehaviour, IShootable

And we’ll just rename PerformExplosion to HandleShot. Now, we change our shooting logic.

if (hit.collider.gameObject.TryGetComponent<IShootable>(out IShootable shot))
{
AudioSource.PlayClipAtPoint(_hitSound, hit.point);
shot.HandleShot();
}

Adding the Force Barrier

We’ll create a a force barrier class and make it an IShootable. Shooting it will deactivate the barrier.

public class ForceBarrier : MonoBehaviour, IShootable
{
public void HandleShot()
{
DeactivateBarrier();
}
}

And what will deactivation do? We’ll fade out the energy barrier, set its layer to not include Obstacle, and then call a coroutine to reactivate it.

private void DeactivateBarrier()
{
// Fade out barrier

// Remove obstacle layer

// Start coroutine to restore it
}

While we’re at it, we’ll a function to activate the barrier.

private void ActivateBarrier()
{
// Fade barrier in

// Add obstacle layer
}

Fading out the barrier is made easy with DOTween.

foreach (Renderer renderer in GetComponents<Renderer>())
{
foreach (Material material in renderer.materials)
{
DOTween.ToAlpha(() => material.color, x => material.color = x, 0, _fadeTime);
}
}

Except…

*sigh* Apparently the custom shader on used on the asset to fade the barrier in and out doesn’t let you modify the base color like that. I could probably make a custom modification for the shader, but I think I’ll skip fading things out, and just disable the Renderer.

foreach (Renderer renderer in GetComponents<Renderer>())
{
renderer.enabled = false;
}

Changing the layer of the force barrier to no longer make it something that blocks the shot is pretty easy.

gameObject.layer = LayerMask.NameToLayer("Default");

Although, in retrospect, if there’s no other aspect that I need it for, disabling the colliders would have also worked… Lastly, we’ll call our coroutine to reactivate the panel after a time.

private void DeactivateBarrier()
{
Debug.Log("Deactivated?");
foreach (Renderer renderer in GetComponents<Renderer>())
{
renderer.enabled = false;
}

gameObject.layer = LayerMask.NameToLayer("Default");

StartCoroutine(DelayedReactivation());
}

The DelayedReactivation coroutine is very simple.

IEnumerator DelayedReactivation ()
{
yield return new WaitForSeconds(_reactivationDelay);

ActivateBarrier();
}

And, lastly, ActivateBarrier.

private void ActivateBarrier()
{
foreach (Renderer renderer in GetComponents<Renderer>())
{
renderer.enabled = true;
}

gameObject.layer = LayerMask.NameToLayer("PossibleCover");
}

And, let’s show that it works by shooting the force barrier, and then shooting the barrel behind it.

Yup, that works. All that’s left is to add a sound for deactivation, and a sound for activation, and it’s a solid part of the game. Which reminds me… we need an explosion sound too…

--

--