Enemies with Shields
Objective: Provide logic for some enemies to have shields. Shields allow the enemies to take one or more free hits.
This new feature of Galaxy Shooter is one of the reasons why I was so keen on doing all that refactoring of LivesComponent. Not only did we allow enemies to have damage effects, but because we put all the shield code in LivesComponent, the enemies are capable of having shields as well!
The main thing that we have to take care of for enemies to have shields, is to actually apply shields to the enemy prefabs.
Each LivesComponent script, for both the player and enemies, have a serialized field that we can use to get access to this shield object and control whether it is on or off. However, if we had tons of different enemies at this point, it might be a little annoying to have to assign every shield object to this field, so it helps to have code that does it automatically.
This helper method will search through the child game objects and try to find one that is tagged “Shield”. That way we can get access to each enemy’s shield object even if we forget to assign it in the inspector.
Now we just need a way to turn on the shields for some of the enemies that appear. A good idea is to have it tied to the number of waves that have been cleared. The more waves that we’ve defeated, the more likely it is to see enemies that have shields.
Now with this new variable set to 1 in the inspector, we’d expect all enemies to have shields by wave 2, when any random float between 0f and 1f will be less than a _shieldedEnemyChance of 1.
Yet when we try it out in game, it doesn’t work! What’s going on here?
This is a tricky problem caused by the execution order of the the scripts. The issue is that the SpawnManager is trying to turn on the shields of the enemies, but then AFTER that happens, the enemy’s Start method is called, which turns the shield back off by default. Start is called on the first frame in which an enemy would update, which will happen after the spawn manager finishes doing its thing.
So how can we make sure that all the default enemy initialization, like keeping the shield turned off, happens during the execution of SpawnManager’s code? It’s actually an easy fix; we can use Awake instead.
Awake is a similar method to Start in that it’s only ever called once by the Unity engine. However, Awake happens right when the gameobject is first instantiated, so it can momentarily stop the SpawnManager to turn the shield off by default, and then afterwards the SpawnManager can properly control whether to turn that shield on or leave it turned off.
With that fix, everything is working as intended, and this game has become a lot more challenging. The earlier refactoring of the LivesComponent was a huge reason why adding this new feature was relatively painless.