Objective: Create a boss and a boss fight scripted like a charm
At the time I started creating what I needed for a boss fight, I went through a deep self-analysis of my skills, my knowledge, my journey. Long story short, I decided my Unity level was not up to my expectations… and I usually expect a lot from myself.
So I started documenting, reading, studying. I found out I was not doing things in the most efficient way, I was not thinking ahead enough.
I stumbled upon a very good book about Design Patterns, applied to game developing (Game Programming Patterns by Robert Nystrom). I will talk more about it in the future. Just know that the first chapters gave me ideas for a better approach to scripting so, before creating the boss fight, I started reworking.
Basically I fell in love with what is called Component Pattern, something we are quite used to in game developing with Unity: think about transform component, or any other type attached to a game object. Each has a purpose, each is separated from the others but can easily communicate with others (do you remember the
So, little by little I took all the stuff I threw in single scripts and reworked it, re-thought it to fit this pattern: I created script for movement, input management, shooting, aiming, animating (mostly exploding)… and so on.
In this way, objects like the player or different enemy types would boast many scripts as their component, each of them has only few tens of lines of code and fulfils one, just one, purpose.
With this base, I started all over again with my primary objective: a badass boss fight!
I will recap shortly what the design would be, but have clear in mind that this design is the result of many trial and error steps:
- The boss is one big ship, so big it doesn’t fit the screen. Fortunately we are prepared to manipulate the camera for a good zoom out effect.
- The boss enters the scene when the player gets to
killTarget — 1amount of kills. At that point all enemies on the scene will fly away. The boss enters slowly from top side until a position is reached and then stays there.
- The boss will be immune to asteroids: they will bounce off.
- The boss will try and destroy the player launching torpedoes from 4 different launcher, 2 at a time from randomly chosen launchers. The launch occurs at a given rate.
- The boss will also shoot down at you laser when you are in front of it (reusing enemy logic) but also sideways, using a similar system.
- Enemy spawn is stopped.
- The boss has a bunch of lives, after a given damage it turns red and shoots more frequently.
This is a glimpse of the boss appearance.
As you can see, there are a few children, which are empty objects used as placeholder for the launching/shooting position.
Now let’s look at the list of components in the boss object, in order to tell which are interesting and worth explaining. Of course we’re going to skip all non-custom components (up to the last Audio Source).
Just a detail: the boss is not a trigger but a collider, such as the asteroids, so they can interact as physical objects (I used the shield physical material).
Enemy script contains logic about collision and damaging, lives and score info.
EnemyTargetingSystem uses boxcast to detect player position in a given direction.
EnemyShooting takes care of the base laser shooting, as the base enemy, just downward.
Explosion is used in all destructible objects to set up the animation logic. If you’re asking why this script cannot be disabled and/or enabled, it’s because it doesn’t have a Start method.
These are the scripts that the boss shares with the common enemy. Now let’s get to the special ones, in detail.
Boss is a sort of master unit managing general purpose logic, such as overriding lives and score value and activating the second phase. Not really worth exploring more.
BossMovement manages the scene entering with an appropriate coroutine.
BossShootingSystem uses the targeting system to detect player position also sideways and shoots consequently, it also has the torpedo launching coroutine.
In BossMovement Script, what’s really interesting is the entering coroutine that is going to be called by a boss manager script outside. All shooting is paused until the position is reached (through a translation in a loop). Also the number of kills is set to constant until the boss is ready: this is to avoid unintentional winning by eliminating one of the other fleeing enemies. The same goes for the boss lives: basically it is invincible until the real fight begins.
But the most exciting part is the one involving the camera: while entering, there is a slow zoom out which exploits the same coroutine, by simple change in
ortographicSize camera property. You’ll see.
In the shooting script, the core is represented by the shooting coroutines: torpedoes and lasers.
The lasers are shot as usual, via targeting system check, but using a different direction (left or right) which will reflect on the instantiation Quaternion parameter. Of course this needed a little change in laser script, but nothing too hard: the flight direction is now a parameter.
Torpedo launching uses a little of random numbers to decide the launching position. The
Launch method is a simple instantiation of torpedo and setting it as enemy.
It all seems child’s play now, but the secret is that in order to write down a logic in few lines of code, with an actually short coding time, hours of thinking and planning were necessary.
That’s all for boss behaviour! Vertical laser shooting is free bonus when adding the base EnemyShooting script, side shooting is somewhat similar if the engage or targeting system is made flexible enough and torpedoes are the only actual novelty here. Explosion, collision etc. are all already managed.
Now we have to tell the boss to enter the scene. For this reason I added a new script to the LevelManager gameobject.
Every 0.5 seconds there is a check on the kill counter. Once the last kill is reached, the asteroid spawning rate is slowed down, enemy spawning is turned off, all remaining enemies are removed and the boss is instantiated.
The enemy removal consists in turning off the respawn at top once they fly all the way down, disabling the collider and shooting (no chance of killing the player while leaving), by accessing spawn manager and shooting scripts. Once the boss is instantiated, the enter movement is started.
That is everything. I tried to battle the boss, it was funny yet quite hard. Good.
Here is how it appears in a sandbox level, without other features.