Dependency injection
2025/12/27
- Type
- Learning Resource
- Format
- Glossary Article
- Version
- General
- Subject Tags
- Created
- Updated
- 2026/02/16
- 2025/12/27
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:
class_name HealthBar extends ProgressBar
func setup(health_changed: Signal) -> void:
health_changed.connect(_on_health_changed)
func _on_health_changed(new_health: int) -> void:
var tween := create_tween()
tween.tween_property(self, "value", new_health, 0.3)The player script has a signal that emits when the character's health changes:
class_name Player extends CharacterBody2D
signal health_changed(new_health: int)
var health := 100: set = set_health
func set_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 extends Node
@onready var player: Player = %Player
@onready var health_bar: HealthBar = %HealthBar
func _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_changedsignal, 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.
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 extends Control
var player: Player = null
func _ready() -> void:
var result: Array = get_tree().current_scene.find_children("*", "Player", true, false)
if not 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.
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.
Get help from peers and pros on GDQuest's Discord server!
20,000 membersJoin ServerThere are multiple ways you can join our effort to create free and open source gamedev resources that are accessible to everyone!
Sponsor this library by learning gamedev with us onGDSchool
Learn MoreImprove and build on assets or suggest edits onGithub
Contributeshare this page and talk about GDQUest onRedditYoutubeTwitter…