Enemy Shields

Simon Pham
3 min readJul 27, 2023

--

In this blog post, we’ll explore adding a shield to random enemies that can withstand one hit from the player.

Let’s begin by creating a shield for our enemies. We can duplicate the player’s shield and change the color to green like this:

I’m using particles, so I’ll modify the Start Color property in the Particle System to change colors.

The shield should be set up as a child GameObject of the enemy prefabs.

Similarly to the player, we’ll hide the enemy shields, and we will activate them through code.

In the Enemy script, let’s create a variable for the enemy shields and assign each of them in the Inspector:

[SerializeField] private GameObject _enemyShield;

Also, we need to create a boolean variable for Unity to determine if the enemy shield is currently active:

[SerializeField]  private bool _isEnemyShieldActive = false; 

Currently, all of our logic to handle collisions is inside the OnTriggerEnter2D function. To make our code more reusable, we will create a separate function called DamageEnemy and move the related code into that function:

public void DamageEnemy()
{
if (_isEnemyShieldActive)
{
_enemyShield.SetActive(false);
_isEnemyShieldActive = false;
return;
}
_player.CalculateScore(10);
_enemyExplosionAnim.SetTrigger("OnEnemyDeath");
_audioSource.Play();
_speed = 0;
_isDead = true;
Destroy(_enemyCollider);
Destroy(this.gameObject, 2.3f);
}

When the shield is active, enemies won’t take any damage, and their shields will be destroyed. Now, our OnTriggerEnter2D function looks more readable:

private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
DamageEnemy();
_player.Damage();
}

if (other.tag == "Laser")
{
DamageEnemy();
Destroy(other.gameObject);
}
}

Let’s manually unhide the shields in the Inspector and test our game.

The enemy shields are functioning properly. Now, let’s work on creating two functions to activate and deactivate shields that we can publicly access from other scripts:

public void ActiveEnemyShield()
{
_enemyShield.SetActive(true);
}

public void DeactivateEnemyShield()
{
_enemyShield.SetActive(false);
}

In the SpawnManager script, let’s get a reference to each of the enemy types and null-check them:

private Enemy _regularEnemy;
private Enemy _mediumEnemy;
void Start()
{
_regularEnemy = _regularEnemyPrefab.GetComponent<Enemy>();
_mediumEnemy = _mediumEnemyPrefab.GetComponent<Enemy>();
if(_regularEnemy == null )
{
Debug.LogError("Regular Enemy is null");
} else if (_mediumEnemy == null)
{
Debug.LogError("Medium Enemy is null");
}
}

We’ll apply the same logic as we did for the enemy movements and power-ups to statistically spawn enemies with a shield. Here’s an example of spawning regular enemies with a 50% chance of having a shield:

int _enemyShieldProbability = Random.Range(1, 101);
if(_enemyShieldProbability <= 50)
{
_regularEnemy.ActiveEnemyShield();
} else
{
_regularEnemy.DeactivateEnemyShield();
}

And here’s the full code for the SpawnEnemies function:

private void SpawnEnemies()
{
Vector3 posToSpawn = new Vector3(Random.Range(-9.3f, 9.3f), 6f, 0);
int _enemyTypeProbability = Random.Range(1, 101);
int _enemyShieldProbability = Random.Range(1, 101);
if(_enemyTypeProbability <= 60)
{
GameObject newEnemy = Instantiate(_regularEnemyPrefab, posToSpawn, Quaternion.identity);
if(_enemyShieldProbability <= 50)
{
_regularEnemy.ActiveEnemyShield();
} else
{
_regularEnemy.DeactivateEnemyShield();
}
newEnemy.transform.parent = _enemyContainer.transform;
_enemy = newEnemy.GetComponent<Enemy>();
CalculateEnemyMovementProbability(_enemy);
} else
{
GameObject newEnemy = Instantiate(_mediumEnemyPrefab, posToSpawn, Quaternion.identity);
if (_enemyShieldProbability <= 50)
{
_mediumEnemy.ActiveEnemyShield();
} else
{
_mediumEnemy.DeactivateEnemyShield();
}
newEnemy.transform.parent = _enemyContainer.transform;
}
}

Let’s test our game:

And it’s working as expected! In the next blog post, we’ll explore creating an aggressive enemy type that charges towards the player when they are in range.

--

--

No responses yet