Today’s the day! We’re going to implement the our invincible boost power-up!
To implement this power-up, there will be 2 things that we will need to implement:
- Making our plane move faster.
- Making our plane invincible so that we won’t die if we collide against anything.
Making the plane move faster isn’t hard, however the big problem is implementing invincibility. We want to prevent game over when we collide against an obstacle, however if we were to do that, we’re going to have a problem with being able to go off stage.
Either way it’s going to be an interesting problem, but let’s tackle them one at a time!
Step 1: Make the Plane Move Faster
Step 1.1: Change the plane speed in PlaneController
Introduction
It’s been a long time since we last looked at this script, but if we’re looking at changing movement speed, the script that does that is PlaneController
located in the Player
game object (and sibling of our favorite PlaneCollider
script).
We’re adding a very simple change to our script:
using System;
using UnityEngine;
public class PlaneController : MonoBehaviour
{
private Camera _mainCamera;
void Start () {
_mainCamera = Camera.main;
}
void Update ()
{
switch (PlayerManager.Instance.CurrentState)
{
case PlayerManager.PlayerState.Alive:
MovePlayer();
break;
}
}
private void MovePlayer()
{
Vector3 movement = GetMoveSpeed(_mainCamera.transform.rotation.x,
_mainCamera.transform.rotation.y);
Vector3 forward = transform.forward;
if (PlayerManager.Instance.ContainsPowerUp(PlayerManager.PowerUpType.Invincible))
{
forward *= 2;
}
transform.position += (forward + movement) / 2;
}
private Vector3 GetMoveSpeed(float x, float y)
{
float xMove = Mathf.Min(Mathf.Abs(y * 10), 3);
float yMove = Mathf.Min(Mathf.Abs(x * 10), 3);
if (x >= 0)
yMove *= -1;
if (y < 0)
xMove *= -1;
return new Vector3(xMove, yMove, 0f);
}
}
Walking Through the Code
- Like what we did with the score multiplier, in
MovePlayer()
if our player has the invincible power-up, we take our base movement (forward
), double it, and then re-apply it back to our existing code.
That’s it! Now whenever we pick up the invincible power-up, we will move twice as fast as we do.
Step 2: Make the Player Invincible
Step 2.1: Adding the Invincibility Effect in PlaneCollider
Now that we have the speed boost, the next thing we need to add is the ability to not die in the game when we collide against anything.
Accomplishing this isn’t difficult, as we will soon see, the real problem will come after this!
We only get a game over whenever we collide against the walls in our path and that code is located in PlaneCollider
!
Here’s what it looks like now:
using UnityEngine;
public class PlaneCollider : MonoBehaviour
{
…
private void EnemyCollision()
{
if (!PlayerManager.Instance.ContainsPowerUp(PlayerManager.PowerUpType.Invincible))
{
Instantiate(Explosion, PlaneObject.transform.position, Quaternion.identity);
Destroy(PlaneObject);
PlayerManager.Instance.GameOver();
GameUIManager.Instance.GameOver(gameObject);
CameraManager.Instance.GameOver();
}
}
}
Walking Through the Code
- In
EnemyCollision()
(which gets called when we collide against cube), we add an if
check to see if we have picked up an invincible power-up and if we haven’t, then we will crash and get the game over screen like normal. If we have picked up the power-up, then we won’t go into our if
statement and live.
With that, if we have the invincible power-up, we’ll never be able to die. Which is great, except for this:
As you can see, we can fly off the stage…
Step 2.2: Preventing the Player From Flying Out of the Path, Fixing Our Trigger Collision
So how are we going to fix this? I thought about it for a while, but in the end, the solution to this problem was simple. We need to flip the way we’ve been handling our collision logic.
Currently in Plane Collider, we have a capsule collider with the Is Trigger option checked. When this is checked, it means that our plane will ignore any physics in the game and continue to fly through everything.
Before we didn’t have any problems, because the moment we hit a wall, we died, and we would never go through anything, however now that we’re invincible, we can no longer say that’s the case.
However, we can’t simply just convert everything to be a collision (non-trigger), because we don’t want to bump into a power-up/coin. Instead, we must separate the logic.
We must separate our collision logic to:
- Use triggers (what we already have) to pick up coins and power-ups
- Use collisions for bumping against walls
Currently, our logic works because our plane is set to be a trigger, however if we were to change that, most of our code won’t work anymore.
Here’s what we must do to solve this:
- In Capsule Collider component in Player, uncheck the Is Trigger, this will make enable us to bump into objects and prevent us from moving, however this will break our original coin/power-up code as nothing is a trigger anymore. Let’s fix that.
- Go to our Magnet prefab (drag it into the scene if it’s not there) and in the Box Collider component, check Is Trigger. Make sure to hit Apply to save our changes to all our prefabs.
- Go to the 2x > 2x Model prefab and in the Box Collider component, check Is Trigger. Make sure to hit Apply to save our changes to all our prefabs.
- Go to the Rocket > rocket prefab and in the Box Collider component, check Is Trigger. Make sure to hit Apply to save our changes to all our prefabs.
- Go to the Coin Container prefab and in the Box Collider component, check Is Trigger. Make sure to hit Apply to save our changes to all our prefabs.
With these changes in now, we have the trigger collision functionality.
However, we have one more thing that we should change. This will become obvious the moment we make our changes below, but when we collide against something, our plane will rotate because of the collision (and cause massive dizziness!).
We don’t want that, so to fix that, we’re going to fix our rotation in our RigidBody
component.
- In the
RigidBody
component in Player
, under Constraints > Freeze Rotation check and freeze X
, Y
, and Z
so the plane’s rotation can never be changed.
Step 2.2: Preventing the Player From Flying Out of the Path, Fixing Our Collision
The next thing we have to do now is move our wall collision to be in the OnCollisionEnter()
entry point.
The difference between a collision and a trigger is based off the object that you’re colliding against. A trigger isn’t affected by physics and a collision is.
What we need to do to make this work is that we need to move our Wall collision logic from the OnTriggerEnter()
to OnCollisionEnter()
. Here’s our change to the PlaneCollider
script:
using UnityEngine;
public class PlaneCollider : MonoBehaviour
{
public GameObject PlaneObject;
public GameObject Explosion;
public AudioClip MagnetSFX;
public GameObject MagnetParticleEffect;
public AudioClip MultiplierSFX;
public GameObject MultiplierParticleEffect;
private SoundManager _soundManager;
private GameObject _currentParticleEffect;
void Start()
{
_soundManager = GetComponent<SoundManager>();
}
void OnCollisionEnter(Collision other)
{
CheckUnTaggedCollision(other.gameObject);
}
void OnTriggerEnter(Collider other)
{
switch (other.tag) {
case "Coin":
CoinCollision(other);
break;
case "Magnet":
MagnetCollision(other);
break;
case "Score":
ScoreColllision(other);
break;
case "Invincible":
InvincibleCollision(other);
break;
}
}
private void CheckUnTaggedCollision(GameObject other) {
if (other.name.Contains("Cube")) {
EnemyCollision();
}
}
private void EnemyCollision()
{
if (!PlayerManager.Instance.ContainsPowerUp(PlayerManager.PowerUpType.Invincible))
{
Instantiate(Explosion, PlaneObject.transform.position, Quaternion.identity);
Destroy(PlaneObject);
PlayerManager.Instance.GameOver();
GameUIManager.Instance.GameOver(gameObject);
CameraManager.Instance.GameOver();
}
}
}
Walking Through the Code
It feels like it’s been a while since we had a more complex code change, huh? Here’s what our changes do:
- In
OnTriggerEnter()
, we removed the case where we bump into the walls (an untagged collision), but we kept everything else the same. - To replace our wall collision, I added
OnCollisionEnter()
and call CheckUnTaggedCollision()
which will handle the object the same way. One thing to note that is different, in OnCollisionEnter
, we receive a parameter that is typed Collision
while the OnTriggerEnter
receives a type Collider
. The documentation on Collision explains the difference, but the main difference is that Collision
contains more information about the physics of the game object we collided against. However, because we have a different type, I changed CheckUnTaggedCollision()
to take in a game
object instead of a - In
CheckUnTaggedCollision()
, I change the passed in type to be a GameObject
which coincidentally has the same API calls that we were using for when we were passing in a Collider
so we don’t have to change much of anything.
With these changes in, we have everything we need now to have our invincible power-up work!
Check it out! Note that we picked up the invincible power-up.
End of Day 95
And that’s it! It turns out that implementing the invincible power-up took more work than we thought, but overall it wasn’t too bad!
We now have a fully functional invincible power-up in our game!
The next and final thing we need to do now is to add sound and special effects for our power-up. I know what you’re thinking, this is going to be super repetitive, but don’t worry, there’s a special effect that I want to implement that’s completely different than what we have done in the past!
However until then, I’ll see you later!