Hollow Knight Inspired Boss Fight in Godot 4

Imagine your players claiming that your game is great because of its memorable boss fights.
Hollow Knight and Silksong achieved that level of recognition among their fans, getting compared with the famous Dark Souls series.
The major inspiration for bosses in Hollow Knight, though, is the Mega Man X series. Which is what inspired our little book too. In Mega Man X, we have a clear “stage” for the boss battle that we call boss pit. This approach helps us design a controlled environment to create the boss behavior patterns, allowing us to mix the boss actions with the environment surrounding it, giving a sense of agency and intelligence for the boss.
In this tutorial, we are going to implement a multi-phased boss battle. That means that a given event will trigger a new set of behavioral patterns in the boss, typically making it harder to defeat. For that, we will use Godot’s animation system, especially the AnimationTree node. I recommend reading the documentation of this class, by the way.
Analyzing Hollow Knight Boss Battles
I’m going to use Silksong’s Widow fight because I defeated this boss this weekend, and I’m with my memory fresh about her. Take your time to watch this full footage so we can proceed in the same page.
You will notice that the boss has six patterns:
-
Pulls two bells that fall vertically
-
Pulls two bells that fall diagonally
-
Pulls two bells, one falling vertically, the other falling diagonally
-
Pulls six bells that fall vertically
-
Dive attack diagonally from mid air, followed by a dash attack horizontally on the ground
-
Runs on the ground, pulling four massive sets of blades that appear with a delay
She repeats these patterns throughout the entirety of the first phase, randomly picking them. You can notice there is some sequential pattern sometimes, like when she most of the time dives from mid-air after pulling the blades from the ground. But this is not predictable all the time.
Then, at around 1:42, the player manages to deal enough damage to the boss to trigger the second phase, where a new set of behaviors appears and a new loop is established. More visceral, quicker, more damage, more aggressive.
As you may have noticed, there aren’t many emergent gameplay elements in this boss fight. The boss is going to repeat a pre-established set of behaviors and transition to a new set after a given condition is met. This is the perfect use case for…you guessed it: a Finite State Machine, or FSM for short.
Using the AnimationTree for an Epic Boss Battle
Many people like to create their own implementation of an FSM using all sorts of complex enter and exit states and transitions with hierarchical states. I like to avoid doing my own stuff as much as I can. I prefer relying on tested results that thousands of people are improving. So, whenever I can, I use built-in tools instead of my own. In that sense, if you understand that a state in programming is nothing but a set of values of an object’s properties, and that a state machine simply allows you to easily transit between pre-defined states, you can use Godot’s animations to implement the State pattern, and the AnimationTree to implement an FSM…there’s a whole interface with everything you need for that.

So, to focus on the value proposition of this tutorial, I’ll provide you with a starting project with some behaviors I created for this boss, Aries. Download the project below and you will have the 01.start/ and the 02.finished/ project folders. To follow along, open the project.godot file. By the way, don’t forget to subscribe to my mailing list.
Let’s focus on what matters, the 20% of the knowledge that will give you 80% of this tutorial's value. Open the res://Actors/Aries/Aries.tscn
scene, and add a new AnimationTree as a child node of the Aries node. Toggle off its Deterministic property to avoid unexpected blendings between your animations. Select the Aries → AnimationPlayer as the target animation node in the Anim Player property.

Then, here comes the most important setting we have to do before anything, set the Aries node in the Advance Expression Base Node property. This will allow us to use this node as the starting point for our conditional expressions in the state transitions, accessing all its properties, and most importantly, accessing its child nodes with ease.
After that, create a new AnimationNodeStateMachine resource in the Tree Root property and open the “AnimationTree” tab in the bottom center menu.

From here, create the following sequence of animations. First, we are going to transit from the Start to the RESET animation. Then to the appear animation. From that, to the charge animation. Change the appear → charge transition Switch Mode to “At End” so Aries finishes appearing before charging in the player’s direction.

After finishing charging, Aries will disappear, playing the disappear_fast animation, and then will move back to the center, playing the center animation. Then it will reappear, playing the appear_fast animation. And after that, we reached the epiphany of this tutorial.

After reappearing, we want to add some variation in Aries’ behavior pattern. He will either burst, spawning some explosions around him for a brief moment, or he will charge again. Let’s say there’s a 40% chance that Aries will burst. How can we implement that in this system? Well, it turns out that I have a component called RandomChance, that is specialized in dealing with lucky and chances. There’s an animation that calls its RandomChance.throw_dice() method, and we can access the RandomChance.luck to check the luck generated. Luck in this context is the chances of any given dice’s face showing up multiplied by the face that actually showed up. So if we are throwing a D20 and the number 20 was randomized, then we had a luck of 1.0, which is 5% (0.05) multiplied by 20.
Okay, pretty cool class, Henrique, congratulations. But how does this convert into a transition for the animations in question?
— CONFUSED, You
I’ll first of all recommend you READ THE DOCS. Just kidding, but yeah, read the Expression documentation, link below.
Read the Expression Documentation
I want to highlight a specific part of the documentation, so you understand where we are going:
Associating a base instance allows doing the following:
- Reference the instance's constants (
const) in the expression.- Reference the instance's member variables (
var) in the expression.- Call methods defined in the instance and use their return values in the expression.
With that, we can call any method on the Aries node, including something like…get_node("RandomChance").luck and evaluate an expression using comparative operators like:
get_node("RandomChance").luck <= 0.45
With that in mind, we can use Expressions as advance conditions for any transition. So we can create two transitions from the randomize animation: one to the charge animation if the RandomChance.luck > 0.45
and another one to the burst animation if get_node("RandomChance").luck <= 0.45:

As you can see, if we transit back to the disappear_fast animation from the charge 2 and the burst animations, we create a loop which closes the behavior pattern of our boss' first phase.
Now…how do we make it a multi-phased battle? How can we transition to a new set of behavior patterns?
Transitioning to the Second Phase
Now that you have a grasp of the power we have in our hands with this system, it’s time to add another layer to it. After our players defeat our boss once, we may want to transition to a phase two or maybe…phase three, or four. Nothing can stop you now.
To achieve that, we will create some transitions to the stunned animation. This time, the expression will check if the HealthResource.current_amount is less than 1, meaning zero, which means the player defeated the boss. The full Expression then is get_node(”HealthResource”).current_amount < 1. Then, add this Expression to all relevant animations’ transition to the stunned animation.

Then, here goes the stunned animation will transition to the phase two animaiton. Here, you can add any transitional animation, like your boss screaming, like in Silksong. The stunned → phase two transition must have the Break Loop at End property set to true so that Aries doesn’t get stuck in the stunned animation.
Now, here comes the second epiphany of this tutorial. Instead of creating a transition to another animation from the phase two animation, we are going to make a transition to a new State Machine, which we can call Phase Two.

To edit the Phase Two animations and transitions, you can click on the little pencil on the right side of its block.

From here, you can play around, just like we did in the first phase. In my case, I used a similar approach, making a loop from appear_fast → randomiz,e then it will either go to the charge, burst, or the new, more aggressive multi_charge animation, where Aries charges in all directions, creating explosions as he moves. From any of these three, he goes to the disappear_fast → center → appear_fast and starts all over.
The nice catch here is that from any animation, Aries can be defeatead, using the same approach from the first phase, but now, after finishing the stunned animation, we transition to the End block.

This will end the Phase Two “animation state”. So, going back to the first phase State Machine, we can make a transition from the Phase Two State Machine to the disappear animation, making Aries disappear for good.

And this finishes our tutorial! With that, you have a pretty flexible and intuitive system to create multi-phased boss battles, and any sort of enemy behavior, really. I use this everywhere in my upcoming game for enemies and NPCs.
The Boss Battle Chapter
This tutorial is part of what I want to bring as a new chapter for our Platformer Essentials Cookbook in the Hollow Bot Update. For that, I need your support to fund the new chapters.

If you're ready to bring the depth, polish, and challenge of Hollow Knight-inspired boss battles into your own Metroidvania, now's your chance to make it happen. By supporting the Platformer Essentials Cookbook - Hollow Bot Update through the Hollow Bot Bundle, you'll get immediate access to 3 learning resources (2 books and 1 video course) packed with over 40 tutorials to guide you through creating rich, immersive Metroidvania games in Godot Engine 4.
By achieving our campaign goal, you'll unlock 4 brand-new chapters, including the multi-phased boss battle system you just saw in action. This is your opportunity to not only learn the mechanics that make Hollow Knight unforgettable but to fund the creation of even more advanced content that will level up your game development skills. Grab the bundle, and let's build something incredible together.
Click to get the Hollow Bot Bundle
That’s it, thank you for reading! Keep developing, and until the next one!
— Henrique Campos
Get Godot Platformer Essentials Cookbook
Godot Platformer Essentials Cookbook
Step-by-step tutorials with essential features to create platformer games
| Status | Released |
| Category | Book |
| Author | Ludonauta |
| Genre | Educational, Platformer |
| Tags | 2D, book, Boss battle, Godot, Immersive, Non violent, Open Source, Tutorial |
| Languages | English |
| Accessibility | Color-blind friendly, High-contrast |
More posts
- Making a Bomb for Godot Platformers with the Hazard Recipe44 days ago
- Weekly Update: Playable Demo and Main Chapters47 days ago
- Remove 90% of Conditional Statements and Bugs from Your Game: Strategy Pattern61 days ago
- How to Make an Interaction System in Godot 464 days ago
- 5 Tricks to Enrich Your World Design65 days ago
- Weekly Update: State Machines and Interactive Areas68 days ago
- RigidBody vs CharacterBody: Which One for Your Platformer?84 days ago
- 5 Common Mistakes in Godot 4 Platformer Games86 days ago
- Combat System Update: Hit&Hurt Areas and Bumping Enemy89 days ago
- Slope Movement Is So Hard (unless you use Godot Engine)99 days ago

Comments
Log in with itch.io to leave a comment.
My guy you used an AI as a thumbnail
yes, I always do.
Nice tutorial!
Thanks! Glad you liked it!