Phase 2: New Enemy Type
It is time to bring another enemy into our game, and this one has quite a few curveballs.
Create enemy types that can fire & damage the player.
Create enemy type with unique projectile (ie. Laser beam, heat-seeking, etc)
Unique Movement Behaviour (zig-zag, wave, etc)
In a previous challenge, I had to create a new movement, but I created it so that it was unpredictable and random, and therefore decided that I would rather focus on the first two points of the challenge.
Creating a new enemy was the easiest part of the challenge and decided to start there and work my way towards creating a new weapon type. I took an existing enemy prefab, dragged it to my Hierarchy and unpacked the prefab to be able to make changes before I prefab it again later. I renamed the Enemy to “DangerousEnemy” and replaced the sprite and animation with a new enemy ship. Before I could continue creating the new enemy, it was time to create the new weapon.
Next, I opened the Enemy script to add the logic for the new enemy type and the weapon they will fire.
I have created two new variables, one that will carry the missile GameObject called “_missilePrefab” and another that is a bool to set the enemy type to “_dangerousEnemy”. Currently, we have a Coroutine that would shoot periodic lasers if the bool “_shootingEnemy” is true. We can replicate this behaviour and create another Coroutine that would shoot off lasers when the bool “_dangerousEnemy” is true.
New Enemy Weapon
I decided that the new enemy weapon would be missiles. but that there was a 50% chance that the missile could be a homing missile where the missile would fly towards the player to deal damage and explode.
Getting the missile itself to work was not that hard, but the math behind the homing part of the missile was quite a challenge. I’ve looked at various ways you could change the missile’s z position and there’s something called a Cross Product.
The cross product of two vectors results in a third vector which is perpendicular to the two input vectors. The result’s magnitude is equal to the magnitudes of the two inputs multiplied together and then multiplied by the sine of the angle between the inputs.
But before we get too deep into the math of the homing missile let’s first create the basic logic.
Creating the Missile
To create the missile I dragged a sprite from my assets into the Hierarchy and renamed it “Missile”. I then created and added a RigidBody2d and a PolgonCollider2D. I then created a new script called “Missile” and attached that to the Missile GameObject.
In the script, I created float variables for the speed and rotation speed. Then I created a variable to access the Rigidbody. I also created bool variables to check if the missile has been launched and to set if it’s a homing missile. I also created a variable for the target — this is to tell the missile who to home on and is a crucial part of our cross product. Lastly, I added a variable for the explosion prefab to instantiate when the missile is blown up.
In our void Start Function() we need to get a reference to the RigidBody, as well as on the player’s transform. Then I’ve created a variable that has a Random.Range between 0 and 2 returning a value between 0 and 1. If the value is 1 then the missile will be a homing missile. In the void Update() function is where the missile movement and homing magic happens. Firstly, we check if it’s a homing missile to run the logic of the missile homing on the player, and after that, we make the missile move in a downwards position and if the missile leaves the screen the missile will be destroyed.
If our missile is a homing missile then we need to point the missile towards the player. First, we create a Vector2 variable called “direction” and we get the position of our target and the position of the missile. Then we take the direction and normalize it. Next, we create a float variable called “rotateAmount” and that equals a Vector3.Cross using our direction and transform.up on the z-axis. Lastly, we modify the RigidBody’s angular velocity with our “rotateAmount” times with our “rotateSpeed”.
This may seem confusing or hard to understand, it was for me when I had to implement the functionality and I had to rework through everything a few times to sort of grasp what’s happening.
The last part of our missile that needs to happen is we need to check for various collisions, and this is where we’ll use that “_friendlyfire” bool. There are a few ways to check for collisions but since we’re building a 2D game we’ve allowed objects to “clip” through each other. Because of this, we use something called “OnTriggerEnter2D”, and we’ll be checking if the laser collided with our player, its laser or an enemy. Now when the enemy first shoots a missile it does briefly collide with the enemy, but we don’t check for that collision until the missile has left the enemy ship.
You might be wondering why I would want to check if the missile is colliding with an enemy ship or better put why would a missile destroy the ship that shot it. Well, I wanted to incorporate a bit of fun in the game and since the player had the ability to fly faster for a brief moment I thought if the player chooses that it could trick the missile into flying into an enemy. Alternatively, the player could try and shoot the missile out of the air.
No seriously, we’re almost done. Once the script has been saved it’s time to head back to unity and set everything up.
On the Missile, attach the explosion prefab that we’ll instantiate when the missile is destroyed. Once that’s done we’ll drag the missile to the Prefabs folder. Next, you’ll select your Dangerous Enemy and drag the Missile to the missile slot and check the “DangerousEnemy” bool and prefab the new enemy. Now at this point, there’s something important to take note of here. We have three different enemies. But all three enemies use the same script, and therefore all three enemies will need to have that Missile prefab assigned otherwise you will get null errors. This would be a good time to start creating scripts for the individual enemy ships but inheriting from the Enemy script, but that’s for another article.
I hope you were able to follow what was being explained in this article. It was a bit hard for me to understand everything myself but it’s not as hard as it seems. Practice makes perfect.