Dependency injection is a pattern where an object receives the data it needs to function (its dependencies) from outside, rather than creating them itself. Instead of a class creating its own dependencies, you pass them in, typically through the _init() constructor method or a custom setup() method.
In game development, this pattern is common because game entities often need to reference other objects, resources, or signals. Rather than having each object find or create what it needs, you pass references around. This is already a common practice in Godot, even if we usually don't call it "dependency injection."
Let's use a common example: in your game you have different characters that the player can control with a heads-up display showing health, stamina, and other stats. Instead of having the UI search for the player character in the scene tree, you can inject the player reference into the UI when you set it up.
Here's a health bar. It has a setup() function that expects a signal:
The player script has a signal that emits when the character's health changes:
class_name Player extendsCharacterBody2Dsignalhealth_changed(new_health:int)var health :=100: set = set_health
funcset_health(new_health:int)->void:var old_health:int= health
health = new_health
health_changed.emit(health)
In practice, you might have a third script that holds references to both the player and the UI, and calls the setup function to connect them:
class_name Game extendsNode@onreadyvar player: Player =%Player@onreadyvar health_bar: HealthBar =%HealthBarfunc_ready()->void:
health_bar.setup(player.health_changed)
This way, the main game script (or whatever you call it) wires up the dependencies. The HealthBar doesn't need to know where the player is or how to find it, and the Player doesn't need to know about the UI. This is just one example; usually in a solo game with a single playable character, you can directly make the UI a child of the player scene and skip this extra step.
You may hear people say that dependency injection helps to decouple different parts of your code. Because, in the UI's example, without the signal, you may directly update the health bar from the player code and create a hard, explicit link between the UI and the player.
But actually, the health bar still depends on the player through its health_changed signal, plus the health bar only exists to display the player's health. So, there's still an irreducible dependency between the two parts of the code. Plus, in this example, the game script has references to both the player and the UI!
When you use a dependency injection, you are explicitly stating that one part of your code relies on another part. Even if that other part is passed in from the outside, the dependency still exists. It's just obfuscated a bit.
Nathan
Founder and teacher at GDQuest
What would it look like without dependency injection?
Without dependency injection, the UI either has to create a copy of the player character or it searches for it by itself.
Creating a new copy of the character doesn't make sense in this case, so instead it would search for the player node with code like this:
class_name HealthBar extendsControlvar player: Player = null
func_ready()->void:var result:Array=get_tree().current_scene.find_children("*","Player", true, false)ifnot results.is_empty():
player = result.front()as Player
if player != null:
player.health_changed.connect(_on_health_changed)func_on_health_changed(new_health:int)->void:# ...
The find_children() method is just one reliable way to find the player regardless of its name or position in the scene tree; you could also find the player node by saving a reference in a singleton, using node groups, or using get_node() with a hardcoded path.
In any case, while this works in smaller games, in a team you'll generally want to avoid this pattern to make the UI easier to replace and the flow of dependencies clearer to your teammates.
Become an Indie Gamedev with GDQuest!in GDSchool
Don't stop here. Step-by-step tutorials are fun but they only take you so far.
Try one of our proven study programs to become an independent Gamedev truly capable of realizing the games you’ve always wanted to make.