Duplicate Godot custom resources deeply, for real
18. March 2025
Resources in Godot are really powerful and can make a Game Devs life easier in many ways. If you haven't used them yet, I can only recommend reading the official docs or my blog post about them.
Now I use them a lot, especially in bigger projects with many different data models. At some point, you might want to duplicate a resource using the duplicate() method. Here things got complicated for me.
Reading the documentation would have saved me hours of debugging, and writing this blog post. But I didn't expect that their duplicate() method works much different as in other object oriented programming languages.
The full source code in this article can be found on Coderberg and on Github.
Note: This blog post was written using Godot 4.x (version 4.4 to be precise) and things could break with further versions.
Create custom resources
I will simply reuse some reduced resources from my open-source game 99 Managers Futsal Edition. There we have futsal players with a name and surname. (For who doesn't know Futsal yet, it's 5 a side soccer indoors)
class_name Player
extends Resource
@export var name: String
@export var surname: String
# custom resources need a _init() method with default values
# to work as expected inside Godot
name = p_name
surname = p_surname
Let's see how Resources can be duplicated in Godot.
Simple references
The most trivial way to make two resources from one, is to assign it to a new variable.
var player_1: Player = Player.
var player_2: Player = player_1
This creates a reference of the resource that still points to the same resource and no duplication takes place. This can be useful if you want to keep a pointer to a nested resource for easier access.
But how can we verify, that they are the same? Every resource in Godot has an unique id, aka RID, that can be accessed with the get_rid() function.
# True, since both have the same RIDs
== player_2. )
# changing player_1's properties, will automatically also change player_2's properties
player_1.name =
player_1.surname =
# True, because both variables point to the changed resource
Shallow copy with duplicate()
Using duplicate() function creates a copy of the resource. This function is also present in other classes, like Dictionary and Array.
To show how duplicate works, a more complicated resource is needed. So let's create a team with a goalkeeper, that is a player.
class_name Team
extends Resource
@export var name: String
@export var goalkeeper: Player
@export var field_players: Array[Player]
Array[Player] = [],
) -> void:
name = p_name
goalkeeper = p_goalkeeper
field_players = p_field_players
Important to know is that only resources with the @export annotation get deeply duplicated. Additionally, this method will fail if _init() has been defined with required parameters.
Now let's create a team and see how it duplicates.
# Let's instantiate a team with a goalkeeper
var team_1: Team = Team. )
# and a copy of it
var team_2: Team = team_1.
team_2.name =
# True, because both teams are unique and independent resources
# Let's change team_2's goalkeeper name
team_2.goalkeeper.name =
# True, because both are named Gianluigi
# Let's change team_2's goalkeeper surname
team_2.goalkeeper.surname =
# Also true, because the team_2's goalkeeper is a reference of the team_1's goalkeeper
So duplicate() creates a unique and independent resources, but all sub-resources will be shallow copies. This means they are only references.
Deep copy with duplicate(true)
The duplicate method has a boolean parameter called subresources, if set true, all sub-resources are also duplicated. So when using the same code from the previous section, the goalkeeper will also be duplicated.
# Let's instantiate a team with a goalkeeper
var team_1: Team = Team. )
# and a copy of it, with sub-resources set to true
var team_2: Team = team_1.
team_2.name =
# Still true, because both teams are unique and independent resources
team_2.goalkeeper.name =
# True, because both still are named Gianluigi
team_2.goalkeeper.surname =
# True, because the team_2's goalkeeper NO LONGER is a reference of the team_1's goalkeeper
Real deep copy
Great, we can now duplicate resources now with all properties. Well, actually not all of them.
The documentation reads: "Subresources inside Array and Dictionary properties are never duplicated." So there is currently no way to deeply duplicate a resource, with ALL it's sub-resources, using Godot's methods.
# Lets create once again two players
var goalkeeper: Player = Player.
var player_1: Player = Player.
var player_2: Player = Player.
# and a team with the two players
var team_1: Team = Team.
Now we have a resource (team) with an Array of sub-resources (players). Let's make some tests.
var team_2: Team = team_1.
team_2.name =
# lets change a players name
team_2.players[0].name =
# True
# True, because both player arrays still are the same resource
So let's create a custom method to cover this special case.
var copy: Team =
copy.players = []
for player: Player in field_players:
copy.players. )
return copy
Using this custom duplicate_deep() method, the tests from above will work as expected. The same approach works with Dictionaries.
var team_deep: Team = team_1.
team_deep.name =
# lets change a players surname
team_deep.players[0].name =
# True
# True, because both players are unique resources
Here you can find the official documentation of the duplicate method.
In the meantime, my code changed
When I started writing this, I had the necessity to deeply duplicate resources. In the meantime I refactored parts of the code to avoid using duplicates.
This makes everything a lot easier to understand and to maintain. I still want to share how it is possible, but highly recommend to check twice if it is really needed.
Said that, I also highly recommend to read the official documentation, as much as possible. Godot has a really amazing documentation, with the class reference also included offline in the editor. So even with no/low internet connections, you are covered.
And to be honest, if I should have read the duplicate methods documentation better in the first place.
Summary
So the most important things to keep in mind when duplicating resources are
- _init() with default parameters
- Only @export annotated properties get duplicated
- duplicate() creates copy of built-in properties, but references to sub-resources
- duplicate(true) creates copy of all properties also sub-resources, except Arrays and Dictionaries
- A custom method is needed, to duplicate Arrays and Dictionaries
Every feedback is welcome
Feel free to write me an email at info@simondalvai.org and comment on Mastodon or HackerNews.