Custom resource is not automatically deleted when the owner is free

Godot Version

4.4 Beta 3

Question

I have a node (Player) referencing a custom resource, at a point in the game the node dies and transitions to a game over screen, on this screen there is nothing referenced to the custom resource and only the game over scene exists at this point.

But when I ‘restart’ the scene as how it was at the beginning, player still holds the same data before it was killed

Any idea what is causing this issue?

Thanks in advance.

Unless you have freed the player object it will still hold the reference.
You will have to reset it.

How do i freed the player object and not the node?

When you change a value related to the custom resource, do you change it in the custom resource or a variable in the Player node?

You don’t free the player (if you don’t want to).
You must reset the resource variable.

class_name Player  
@export var my_custom_resource:PlayerData    
var my_int:int
var my_string:String
var my_float:float

func _ready()->void: 
   init_values()

func init_values()->void: 
   my_int = my_custom_resource.some_int
   my_string = my_custom_resource.some_string
   my_float = my_custom_resource.some_float  

func restart_me()->void:   # call this or just call init_values() when you restart
   init_values()

In the custom resource

I’m already doing that in the absence of any other solution, but I was hoping that if I free, the player would be enough to completely reset it.

I think this is the problem. If you change the value in the resource, it will load with the last value assigned to it.

Let’s say you have a Stats resource and it has a health variable. If you decrease the health value in the resource to 5, it will load with 5 when you call it again.

Usually we declare a variable in the object that uses the resource, then you change the values in the object variable, not in the resource. In the previous example, you would assign the health value from the resource to the health variable in the player. Whenever the player health changes, you change the variable from the player, not the resource.

Resources are basically “data storage”. We often use it to save stats, game settings, saves, etc. You don’t change it unless this is the intended behavior (you want to level up the character, update the game settings, save the game with the last info, etc).

Hmm, this is interesting: since resources are ref-counted, they should be freed when no reference is set. Even the documentation says

When a Resource is no longer in use, it will automatically free itself. Since, in most cases, Resources are contained in Nodes, when you free a node, the engine frees all the resources it owns as well if no other node uses them.
Resources — Godot Engine (stable) documentation in English

So I played around with it and had the same experience as @Fryker. That is, freeing the parent does not free the resource.
However, this is only the case when setting a resource via an exported property or preloading it. In this case there seems there is always a reference to it, independent of the owning node being in the tree. The reason for this is that it is loaded when the when the owner script is parsed. You can check that by looking when the _init function of the resource is called. So as long as there is a reference to the owner scene/script, there will be a reference to the resource.

In any case, if you want the resource to be initialized and freed with the node, you can load the resource via the script (load not preload). In this case freeing the owner frees the resource (if it’s the only one holding a reference to it).

An alternative is to set the resource Local to Scene, then it is duplicated for every Player and freed with it. But this might not be what you want.

Edit: Clarified why there is a reference.

1 Like

Thanks to you I found the problem, the player controller node loads a reference to the Player when starting the game

const PLAYER := preload('res://game/actors/player/player.tscn')

This causes the resource to be created, I guess, and since the player keeps a reference to the resource, is the resource always kept in memory? Even if the player node is not in the scene, since the player’s scene reference is always loaded in memory.

I think

Oh yes, I forgot about preloading (I’m using C# where this is not possible). When preloading the scene is loaded when the script is parsed (similar to when set via @export).

As I understand it, the ‘problem’ is not preloading the player scene, but preloading the resource. You can preload the player scene. As long as you do not preload the resource (or set it Local to Scene when exported), then when freeing the player instance, the resource is also freed.

1 Like

setting local to scene doesn’t seem to fix it when I preload the player scene

I think I misunderstood your question - my bad. I thought that your problem is that the resource is not freed when the player is freed. But you actually want to reset the resource on the same player instance - right?

In this case you could either do it like @sancho2 suggests or you use duplicate to duplicate the resource every time you want to reset it (this is basically what Local to Scene does when the scene is instantiated).

No, no, no, you understood my problem completely, it’s my fault for not expressing myself correctly and not understanding the suggestion, setting the resource to local to scene causes it to be deleted when freeing the player, which is what I was looking for, thank you very much, in your opinion is it better to preload the player or load it when I’m going to add it to the scene.

Ok, so it worked?

I would preload your player scene. This way it is loaded in the beginning and will not impact performance when the owning scene is instantiated the first time.

More information on load vs. preload: Logic preferences — Godot Engine (stable) documentation in English