Adding laser attacks to our boss
In this article we’ll look at adding multiple different laser attacks to our Boss1 script.
As always, I’ll start with the variables.
We’ll need a prefab for the laser game object as well as variables to store the firing positions and rotations for each laser fired.
Our boss game object rotates around, so the tips where the lasers will be fired out of are going to have changing Vector3 values.
I also have an array for storing “targets” other than the player to fire at.
This is for scripted shot patterns.
Two of our laser attacks will use the same targets shown above.
You could keep these targets alive as game objects whose renderers are turned off, however I chose to note down their transform positions and code those as Vector3 values in our _laser3Targets[] array.
Using game objects and a circular sprite to make sure the movement would look more smooth, I made sure to use enough targets that even though the movement is jerky, it will not be extremely jerky.
Further smoothing could be added, and if you used less targets required, but I’m happy with the “look and feel” of these attacks as is.
Once I had all the positions written down, I deleted the big circular sprite and all of the game objects.
We’ll see these used later in the PopulateLaser3AttackTargets() method.
We also have our laser delay variables to control the timing between shots and a general _LaserGo[] array to hold the instantiated laser prefabs.
Update()
As with all of our boss actions, the laser attack is triggered in our Update() method only when another boss action is not in progress.
There is a 30% chance that the laser attack will be triggered.
Once triggered, it calls the StartRandomLaserAttack() method.
This method adds more variety to our boss attacks by randomly selecting from four different laser attacks.
There is a 20% chance for attack 3A, 10% chance for 3B, 30% chance for 2, and a 30% chance for 1.
Orchestrating laser attacks with coroutine delays
Before we dive into each attack, let’s lay the groundwork with some methods these attacks will utilize.
We don’t want the boss to fire one laser right after the next, or even worse, simultaneously.
To control the timing of our laser fire, we need to use coroutines and timed delays.
These 2 routines are nearly identical, so really they could be combined into 1, and have a passed variable that tells the coroutine whether to _rotateTowardsPlayer or _rotateTowardsTarget.
However, I feel I’m sinking too much time into this boss wave as it is and I know the code is currently working so I will leave them as two separate methods.
They get several variables passed in.
First of all, the laser prefab that has been instantiated into two game objects for laser1 and laser2, a delay timing, and whether this is the last laser burst fired for the attack.
Once the delay timing that was passed in from our LaserAttack() methods has passed, we call the FireLasers() method.
If lastBurst is true, we stop rotating our boss game object towards either the player or a target, but rather rotate the boss game object to look down using RotateToLookDown() method.
We then set our _bossActionInProgress flag variable to false after a small 1 second delay.
The FireLasers() method
Our FireLasers() method is where we tell our laser game objects that were instantiated for the current burst of fire to spawn at the appropriate locations on our boss game object, give them a direction, and set their speed.
Modifying the Laser script
While our laser script already had SetSpeed(), we needed to add a public method for direction.
Back to the Boss1 script
Our PopulateLaserGOArray() method takes in the number of lasers needed for the current attack, and then instantiates that number of laser game objects using the _laserPrefabGO.
If this array is populated, we clear out just to keep things tidy and avoid potential issues.
When we create the lasers, we position them off of the player screen and rename the tag to “EnemyLaser” since this laser prefab is also used for the Player’s lasers.
The last “foundational” laser method we’ll look at is the PopulateLaser3AttackTargets() method.
As I mentioned earlier, I positioned game objects in a pattern I wanted to follow, then wrote down the transform positions of each game object.
It’s important to note that you need to input the correct location in the firing order you desire.
Since our type 3 laser attack will run through from 0 to 12 each location, they need to be in the right order.
Now that we’ve covered the foundational methods that make our lasers work, let’s look at how we use them to create different laser attacks.
LaserAttack1()
This attack is very straightforward.
Just follow the player, and burst fire 2 lasers at the correct intervals.
The first thing to do is set _rotateTowardsPlayer to true and populate our laser game object array.
Once the delay we set for tracking the player without firing has ended, we start firing using the LaserAttackDelay() covered earlier in this article.
It is important that we remember to set the “laserBurst” input variable to “true” in our last laser burst.
LaserAttack2()
LaserAttack2() is very similar to LaserAttack1(), it just tracks the player a little longer and fires more bursts more rapidly.
LaserAttack3A() and LaserAttack3B()
Our 3rd attack is divided into an A and B because adding a variation to it is very simple.
As we showed in the PopulateLaser3AttackTargets() method above, we have a half-circle pattern of targets we just run through from top left to top right.
Doing the same thing in reverse order gives us an attack from top right to top left.
Easy variation to add interest to our randomized attacks.
Each method starts like Attacks 1 and 2, populating our laser game object array.
After that, we go a different route.
We do not track the player, but rather start targeting the positions we’ve saved in the _laser3Targets[] array.
We then aim at the next target and fire, rinse and repeat, with the appropriate delays passed into LaserAttackTargetDelay() method.
That’s it, our boss is ready and set to use our laser attacks.