You’re looking for a way to handle data and/or create flexible data objects in your game.
Resource class is a powerful tool for storing and working with data. Many of the most common objects you work with in Godot extend the Resource type: animations, collision shapes, images, etc. Resources not only contain data, but can also manipulate that data (if you’re familiar with the concept of scriptable objects in Unity, the concept is similar).
In addition to all of Godot’s built-in
Resource types, you can create your own custom resources to handle your own game data. This has the benefit of abstracting and encapsulating data - creating something that can be used by any other object in your game.
For this example, we’ll take the concept of player health in a platformer game. A lot of game systems interact with the player’s health. For example:
In addition, there may be other interactions: maybe the game’s soundtrack changes as the player’s health gets low, or enemy behavior changes based on the player’s status.
This is an intentionally simplified example. In practice, you’ll probably need more functionality than we use here, or you’ll want to modify the example to fit with your game’s architecture.
First, we need to define our new custom resource, which we’ll call
PlayerHealth. This resource needs to keep track of properties related to health. It also provides some functionality and signals to handle the changing of the health amount (such as healing and taking damage).
In the Script tab, choose File>New Script. Make sure it inherits
Resource and name it
Let’s break it down piece-by-piece:
At the top we have our
extends line and the
class_name we’re assigning to the resource. This name will appear in various places in the editor.
extends Resource class_name PlayerHealth
Next we have a signal that game objects can subscribe to if they need to know when the player’s health changes. You could also add additional signals for the health reaching zero, etc.
These are the properties we’ll be using.
export (int) var max_value var current_value = 0
This function lets us initialize the health to the max value. You might do this at game restart or when starting a new level.
func reset(): current_value = max_value
This function should be called whenever damage is dealt to the player.
func take_damage(amount): current_value = max(0, current_value - amount) emit_signal("health_changed", current_value)
This function should be called whenever the player needs to be healed.
func heal(amount): current_value = min(max_value, current_value + amount) emit_signal("health_changed", current_value)
Here’s the full script:
extends Resource class_name PlayerHealth signal health_empty signal health_changed export (int) var max_value var current_value = 0 func reset(): current_value = max_value func take_damage(amount): current_value = max(0, current_value - amount) emit_signal("health_changed", current_value) func heal(amount): current_value = min(max_value, current_value + amount) emit_signal("health_changed", current_value)
PlayerHealth class is defined, we can make a new instance of it. Click the “New Resource” button at the top of the Inspector:
In the “Create New Resource” dialog you’ll see the long list of resource types. Searching will locate our
Now you can set the desired
max_value and save the new resource as a
Once the resource has been created and saved, we’re ready to use it. Once again, in this scenario we have the following objects:
ProgressTextureto display health
Area2Dthat heals anything that stands in it
TileMaptiles that cause damage if touched
We won’t include all of the code for the game, just the parts that pertain to the health resource.
On the player, we
export a variable to attach the resource via the Inspector. As part of the player’s movement code, it calls
hurt() when the player runs into spikes.
export (Resource) var health func _ready(): health.reset() func hurt(amount): # Called when running into obstacles health.take_damage(amount)
The heal zone (an
Area2D), affects anything inside it that has a
func _physics_process(delta): for body in get_overlapping_bodies(): if "health" in body: body.health.heal(heal_rate * delta)
Finally, for the UI to display the health, we attach the same health resource and connect to its
export (Resource) var player_health func _ready(): if player_health: player_health.connect("health_changed", self, "_on_player_health_changed") func _on_player_health_changed(value): healthbar.value = float(value) / player_health.max_value * 100
Here’s an example of it in action:
Download the project file here: custom_resources.zip