Day 34 — Improvements: health pickup and multi shot

Hey and welcome!

Alright we’re going to be implementing two new features today, first is the health pickup and next will be a new multi shot ability!

Let’s go ahead and get things started by creating a health pickup and doing all the same things that we did for our ammo pickup only this time we’re going to be adding the following method to our Player script:

public void HealPlayer()
{
if (_playerLives == 1)
{
_playerLives += 1;
_leftEngine.SetActive(false);
_uiManager.UpdateLives(_playerLives);
}
else if (_playerLives == 2)
{
_playerLives += 1;
_rightEngine.SetActive(false);
_uiManager.UpdateLives(_playerLives);
}
}

Increasing our player lives is pretty self explanatory but you’ll notice that I’m updating the vfx for our engine failure as well as the UI for player lives since it’s important that the player has that visual confirmation that they are in fact getting healed.

We’re also setting it here so that the player only heals when they have 1 or 2 lives since we want to cap the lives at 3. Then we update our switch statement in our Powerups script one last time:

switch(_powerupID)
{
case 0:
_audioManager.PlayAudio();
_player.EngageTripleShot();
break;
case 1:
_audioManager.PlayAudio();
_player.EngageSpeedPowerup();
break;
case 2:
if (_player.collectedShield == false)
{
_audioManager.PlayAudio();
_player.EngageShieldPowerup();
}
break;
case 3:
_audioManager.PlayAudio();
_player.RefillAmmo();
break;
case 4:
_audioManager.PlayAudio();
_player.HealPlayer();
break;
case 5:
_audioManager.PlayAudio();
_player.EngageMultiShot();
break;
default:
Debug.Log("Something has gone wrong with defining the powerupID");
break;
}

That’s all it takes! You’ll notice as well that I’ve added in an extra case to handle our multi shot powerup which we’ll be implementing the functionality for next.

Multi Shot

Right so the plan for the multi shot here is for it to fire out in an AoE pattern, we also want it to spawn in rarely so we’ll need to updated some code in the spawn manager as well, let’s start things off by creating the multi shot pickup itself similar to the health and ammo one and then add in the following code to our FireLaser() method in the player script:

public bool _isMultiShotActive = false;
[SerializeField]
private GameObject _laserContainer;
if (_collectedTripleShot == false && _isMultiShotActive == true)
{
for (int i = 0; i < 18; i++)
{
GameObject laser = Instantiate(_laser, transform.position, Quaternion.Euler(0, 0, (i * 20)));
laser.transform.parent = _laserContainer.transform;
}
}

Doesn’t look like there’s much here but it does a lot. First you’ll notice the for loop I’ve put in here, it’s important that we make sure it only loops 18 times per fire.

Now here’s the real important part in our Instantiate you’ll notice that I’m making use of Quaternion.Euler instead of Quaternion.identity. This is because I’m wanting to control the rotation of the laser being instantiated so that the angle that it fires off changes as well. We already have the direction of the laser sorted thanks to our Laser script so here when we change the rotation we’re changing the angle that each laser fires off.

So if i is 1 then the z value of its rotation is set to 20 and the laser fires off diagonally to the top left. Then when the value of i is 2 the rotation is 40 and the laser sets off diagonally top left again but slightly lower than the previous and so on. After 18 loops of this each laser completes a full rotation as the value of z reaches 360 and we get a full circle of lasers firing out of the player’s position.

Very cool stuff and slightly complicated, if I was better at maths there’s a lot more that could be done with Quaternions I suspect but this works well for us for now. Oh and we set the parent of the laser to a _laserContainer object similar to our enemy container that we have under our spawn manager object so that it doesn’t clutter up our hierarchy with 18 lasers coming in at once.

Before we get to changin the spawning frequency let’s add this code to our MoveUp() method in our Laser script:

transform.Translate(Vector3.up * _laserSpeed * Time.deltaTime);if (transform.position.y > 10)
{
Destroy(this.gameObject);
}
else if (transform.position.y < -10)
{
Destroy(this.gameObject);
}
else if (transform.position.x > 10)
{
Destroy(this.gameObject);
}
else if (transform.position.x < -10)
{
Destroy(this.gameObject);
}

This just goes ahead and covers all our bases and deletes our lasers when they go beyond the game boundaries in each direction. Spawning in 18 objects every half second with no way to remove them kind of ruins the performance a little.

Right lastly let’s update our SpawnPowerup() coroutine in our Spawner script.

IEnumerator SpawnPowerup()
{
while (_playerAlive == true)
{
float randomValue = Random.value;
if (randomValue < 0.2f)
{
Debug.Log("Chance to spawn multishot");
int multishotChance = Random.Range(0, 6);
Instantiate(_powerups[multishotChance], new
Vector3(Random.Range(-10.7f, 10.7f), 6.2f, 0), Quaternion.identity);
yield return new WaitForSeconds(3.0f);
}
else
{
int randomPowerUp = Random.Range(0, 5);
Instantiate(_powerups[randomPowerUp], new
Vector3(Random.Range(-10.7f, 10.7f), 6.2f, 0), Quaternion.identity);
yield return new WaitForSeconds(Random.Range(3.0f, 7.0f));
}
}
}

The important takeaway here is the Random.value operation we’re doing. This is something new and what it does is return a random value between 0.0f and 1.0f everytime it’s called.

So to reduce the chances of spawning the multishot can only spawn if the value is below 0.2f and even then the Random.Range(0, 6) needs to return with the value of 6 before it can spawn in. There’s definitely smarter and more mathematical ways to go about this but when I found out about Random.value I just had to use it.

Lastly make sure you have the following code in the Player script so that it limits the multishot to 5 seconds, can’t give the player too much of an advantage!

public void EngageMultiShot()
{
_isMultiShotActive = true;
StartCoroutine(MultiShotRoutine());
}
IEnumerator MultiShotRoutine()
{
yield return new WaitForSeconds(5.0f);
_isMultiShotActive = false;
}

That was quite a lot to cover here but I think the end result has turned out pretty well!

Hey and welcome! My name is Connor and my goal here is to put out a daily post for a full year about my game development journey.