-
Notifications
You must be signed in to change notification settings - Fork 0
Adding HUD Elements
- Introduction
- Creating a New HUD Element
- Adding the HUD Element to the Game
- Connecting the HUD Element
- Supporting Game Loading
- Showing HUD Element Only When Drillbert Is Available
- Making It Work with Drilliam
- Next Step
In this part, we will learn how to add HUD elements and how to interact with the Data
system of Dome Keeper.
When planning to add a new HUD element, it's recommended to check out how the original HUD elements work.
💡 Tip: As always when creating new mod content, it's best to look into the content that is already in the game. You can find some of the HUD elements in res://content/hud
.
For this tutorial, we won't go into too much detail on how Godot's UI system works or how the new HUD scene is created. If you are interested in a deep dive into nodes of the Control
class, make sure to check out the official documentation.
🚨 If this seems a bit overwhelming, feel free to copy the content/hud/DrillbertSleepingIndicator.tscn
scene/file from the Example Mod to your first mod.
Let's assume you created the new HUD elements scene, added a script extending HudElement
(more about this here) and set up working values for the HudElement properties.
The new scene is saved at res://mods-unpacked/Raffa-DrillbertSleepIndicator/extensions/content/hud/DrillbertSleepingIndicator.tscn
- now let's add it to the game!
To do this, we need to add some code to our mod_main.gd
file:
func _ready():
ModLoaderLog.info("Done", MYMODNAME_LOG)
add_to_group("mod_init")
StageManager.connect("level_ready", self, "addDrillbertHud")
func addDrillbertHud():
var hud = Level.hud.addHudElement({"hud": "mods-unpacked/Raffa-DrillbertSleepIndicator/extensions/content/hud/DrillbertSleepingIndicator.tscn"})
The Hud.tscn
scene already has a method to add new HUD elements called addHudElement
and we can get access to the Hud by using Level.hud
.
We just need to wait until the Hud is ready.
To do this we connect our script to the level_ready
signal of the StageManager
. This is signal is emitted when the level is ready. We recommend checking out the official docs about signals.
With this code, our new addDrillbertHud()
method will be called each time a new level is ready. When we start the game and start a new game, we can see that the HUD element is added to the game's interface.
There are a lot of ways to communicate between the extended Drillbot.gd
script and our user interface.
One of the easiest ways to do this is by making use of the Data
singleton / class. You can read more about Data
here.
Let's create a new property in Data with this code in our Drillbot.gd
:
res://mods-unpacked/Raffa-DrillbertSleepIndicator/extensions/content/gadgets/drillbot/Drillbot.gd
func _ready():
Data.apply("drillbert.sleeping", false)
This will add a new property to Data
and set it's value to false.
To change this value, we also add new code to the other two functions and replace our print
lines:
res://mods-unpacked/Raffa-DrillbertSleepIndicator/extensions/content/gadgets/drillbot/Drillbot.gd
func goToSleep():
super() # this calls the base class function
Data.apply("drillbert.sleeping", true)
func wakeUp():
super()
Data.apply("drillbert.sleeping", false)
The next step is to listen to changes of this property. As the HUD element is the place where we want to react to a change to this property, this is also where we need to add the following code:
res://mods-unpacked/Raffa-DrillbertSleepIndicator/extensions/content/hud/DrillbertSleepingIndicator.gd
func _ready():
Data.listen(self, "drillbert.sleeping", true)
💡 Tip: By holding CTRL
and left clicking on highlighted function names, you will jump to the corresponding function. Try it with listen
in Data.listen
Above the listen
function in Data.gd
you will find boilerplate code on how to implement the function. We will use this template to build the code to react to the change of the property in our HUD Element script:
func propertyChanged(property:String, oldValue, newValue):
match property:
# ONLY LOWERCASE HERE
"drillbert.sleeping":
if newValue == true:
$Sleeping.show()
$Awake.hide()
elif newValue == false:
$Sleeping.hide()
$Awake.show()
This code will hide and show certain nodes, based on the value of drillbert.sleeping
.
The mod is now working and pretty much "feature complete".
But there are a couple of things we can do to improve it and make it work nicer.
A lot of nodes have serialize
and deserialize
functions that are used to save and load the element.
We can also find those in the vanilla Drillbot.gd
. To support loading games and update our mod correctly, we can extend this function:
func deserialize(data: Dictionary):
super(data) # this calls the base class function
if state == State.SLEEPING:
Data.apply("drillbert.sleeping", true)
This will apply the correct value to our new Data
property and like this, also update the HUD element.
We only want to show the new HUD element if the player unlocked Drillbert as a gadget!
Knowing how Data
works, we can listen to the property drillbot.headcount
as this will change when the player gets the Drillbert gadget.
Let's change some code in our mod_main.gd
:
func _ready():
ModLoaderLog.info("Done", MYMODNAME_LOG)
add_to_group("mod_init")
StageManager.connect("level_ready", self, "listenToDrillbot")
func listenToDrillbot():
Data.listen(self, "drillbot.headcount")
func propertyChanged(property:String, oldValue, newValue):
match property:
# ONLY LOWERCASE HERE
"drillbot.headcount":
if newValue == 1:
addDrillbertHud()
func addDrillbertHud():
var _hud = Level.hud.addHudElement({"hud": "mods-unpacked/Raffa-DrillbertSleepIndicator/extensions/content/hud/DrillbertSleepingIndicator.tscn"})
Drillbert comes with an upgrade to add Drilliam, his sibling. Now we have a new scenario: two sleeping little "Drillberts", one wakes up, the other one is still sleeping. The HUD will update for waking up one of them, but the other is still asleep. This is a bit problematic. So let's update our code to take this into account.
We should change the code in the Drillbots.gd
:
func _ready():
for drillbert in get_tree().get_nodes_in_group("drillbots"):
if drillbert.state == State.SLEEPING:
return
Data.apply("drillbert.sleeping", false)
func deserialize(data: Dictionary):
super(data) # this calls the base class function
if state == State.SLEEPING:
Data.apply("drillbert.sleeping", true)
func setState(to):
super(to)
if to == State.SLEEPING:
Data.apply("drillbert.sleeping", true)
else:
for drillbert in get_tree().get_nodes_in_group("drillbots"):
if drillbert.state == State.SLEEPING:
return
Data.apply("drillbert.sleeping", false)
This way, the icon will only display Drillbert being awake when both of the siblings are awake.
With this change, the mod is complete and we can upload it to the Steam Workshop! We always recommend testing a few different scenarios with a new mod, like saving & loading. Different maps, modes etc. The tips over here can really help you to speed up the process!
✨ Next Step: Distributing your Mod