Boss Fight Implementation Part 2: Anglerfish Lantern Behavior

Now that I have all of the anglerfish assets in game and setup with colliders, my objective for this article is to focus on the functionality of the dangling lantern.

I begin with the script on my lantern object by adding a public method with a simple debug message telling me that the light has been damaged.

I now go into my projectile class to the OnTriggerEnter2D method where the projectile is checking for the Tag of AnglerLight. After sending the debug message to the console, I declare my public AnglerfishLight class locally, and then use the collision to get the script component off the lantern. I decided to use GetComponent on collision rather than in void start being that for most of the game, the player is not encountering the boss, but it still firing lots of projectiles. After null checking the recently acquired script, I call the public method to damage the lantern.

The same approach is taken with the public method on the script attached to the anglerfish target object, and the target is what will actually be dealing damage to the boss!

Now back in the projectile class, the if else statement continues on to check if the next Tag is the AnglerTarget. GetComponent and the public method are called in the same fashion as the lantern.

I test in game to see if the debug commands are sending, and the console tells me that I have a green light to continue!

Ok, back to the AnglerfishLight Class. I need to access the animator on the Jaw object, so a private Animator variable is declared and assigned in Start using GameObject.Find. After finding the animator I null check it to ensure it’s connected.

Down into the pubic damage method on the lantern, I null check the animator on the Jaw before calling it to trigger the JawOpen animation.

Here is what that looks like.

I want to close the jaw after a period of time elapses, so I decide to start a coroutine to handle the passing of time between triggering animations. This will also allow the player to shoot at the only weak spot this boss has! I add a variable for the wait time, because I will most likely want to reassign it to be a shorter value as the fight gets close to the end, making it more difficult for the player to hit the target. Then in the damage method for the lantern, I start the coroutine to handle the jaw animation. The open jaw animation is triggered, the wait time is well…waited, and then the close jaw animation is triggered.

Here is the result of that. Commence boss damage…almost.

I have a Weak Spot object with a looping animation to show the player where to shoot. It is inactive until needed, so a new variable is declared with a Serialized Field to store the GameObject.

Because these objects come into play at the same time being attached to the parent body, I take the easy route here and assign the prefab for the weak spot in the inspector, to the script component attached to the lantern object. Sometimes a simple drag and drop is all it takes!

I null check the Weak Spot object in start to make sure it’s available. The AnglerJawRoutine method is then updated to set the weak spot object to be active when the jaw opens, and inactive when it closes.

Here is an updated video clip showing the ultimate weakness of the boss!

Oh we aren’t done here yet. I still want the light to take three hits before giving way to the jaw opening and the great reveal that follows. I also want the light to dim as the player hits it so they know something is happening at least. An int variable is added to store the health of the lantern, and it is assigned a value of 3. I also add a bool variable to check if the jaw is open, this way I can better control when the player can hit the lantern and trigger the animation. Another variable is declared for the Sprite Renderer on the lantern object, which will let me tint the color of the sprite with the following four color variables.

In the inspector on the lantern object, I click each color variable to assign it’s color. Full white is the same as no tint…use the normal default color of the sprite. The only thing I am setting here is the alpha, which is the bottom bar that visually decreases with each color transition. The alpha is what controls transparency, so I can decrease the transparency of the light until it’s totally off.

At the top of the final damage method for the lantern, the health of the light is reduced by one every time the lantern gets hit. I then use Mathf.Clamp to assign the value of a local variable to remain between a minimum and maximum value. The light health is now assigned the value of the health clamp, which will keep it from diving below zero if the player keeps blasting at the light. After the Debug message, a series of if else arguments handle changing the color of the Sprite Renderer for the light object (decreasing the alpha/ transparency), as the light health depletes. The final else if argument needs two conditions to enter. The health must be zero and the jaw open bool needs to be in it’s default state of false. The light is then set to invisible, the JawOpen bool is set to true, and the coroutine for the jaw animation is started.

Here is a clip I captured from after the lantern health was working, and before the color change was put in.

The health can be seen depleting in the inspector thanks to the Serialized Field attribute.

The final coroutine for the jaw animation has a few additions near the bottom. I add another yield statement to wait for half a second. This will give the jaw time to close before performing the next actions. The health of the lantern is reset to full, the JawOpen bool is flipped back to false and the color is set back to normal. The lantern is ready to be hit again!

Here is how that all looks together.

Thanks for reading along, and I hope to see you in my next article where I get this boss taking damage!

I am an artist and musician, that is currently diving headfirst into game development with C# and Unity3D.