Custom Resource _init() default value conflicting with default value in inspector

Godot Version

Godot_v4.7-stable_win64

Context

In some custom resources I have been using, some values were returning the default value I set in the _init() function of the custom resource despite me setting the value to something else.

This is an example resource where I set the custom_value to 5 by default in the _init() function. I then create and save a resource value_is_0.tres where I set the custom_value to 0.

class_name CustomResource
extends Resource

@export var custom_value: int

func _init(p_custom_value: int = 5):
	custom_value = p_custom_value

However, when I load value_is_0.tres and print custom_value, it prints as the default value of 5.

func _ready() -> void:
	var custom_resource: CustomResource = ResourceLoader.load("res://value_is_0.tres")
	print(custom_resource.custom_value)

image

Question

So what I’ve figured out is that the inspector thinks the default value is 0 instead of 5, so when I set the value to 0, it just thinks I have set it to the default value and not necessarily 0. Then during runtime, it loads in the actual defaul value I set in _init(). This can be easily fixed by setting the default value in the @export var custom_value: int line like so:

class_name CustomResource
extends Resource

@export var custom_value: int = 5

func _init(p_custom_value: int = 5):
	custom_value = p_custom_value

However, this creates the problem where I have to set the default value twice, which is simple here, but I’m worried about resources where I have several variables and eventually running into human error and setting the default values differently on accident. Is there a way to avoid this? Is this intended behavior and if it is, I feel like it should be documented somehow in the Resource page in the godot documentation.

Don’t override _init. Nodes and Resource types do a lot after _init, overriding init especially with arguments is almost always a bad idea. In this case @export is run after _init

See this page in the documentation for some details

If that’s the case, the docs are a little misleading. This is from the Resources page:

class_name BotStats
extends Resource

@export var health: int
@export var sub_resource: Resource
@export var strings: PackedStringArray

# Make sure that every parameter has a default value.
# Otherwise, there will be problems with creating and editing
# your resource via the inspector.
func _init(p_health = 0, p_sub_resource = null, p_strings = []):
	health = p_health
	sub_resource = p_sub_resource
	strings = p_strings

Maybe I’m reading the docs incorrectly, but I am reading from the resource during _ready(). This issue does not occur if I set the custom_value to anything other than 0 in the inspector. If I set it to 1, it will print 1, its only when I set it to 0, that it prints the 5 that’s set as “default” in _init.

edit: Attached picture showing setting resource value to 1.

Non-@tool scripts aren’t fully loaded in the editor so your _init() assignment won’t run when you create a new Resource from it.

They are partially parsed to get some information like which variables are exported and their default value which is the value yo assign to the @export variable. You can see which default value does the variable have with Script.get_property_default_value()

So:

class_name CustomResource
extends Resource

@export var custom_value: int

Will have its default value set to 0 while

class_name CustomResource
extends Resource

@export var custom_value: int = 5

will have it set to 5.

This happens even if the script is a @tool script. Whatever value you set will be its default value. So if we convert the script to a @tool script:

@tool
class_name CustomResource
extends Resource

@export var custom_value: int

func _init(p_custom_value: int = 5):
	custom_value = p_custom_value

custom_value default value will still be 0 but, because _init() is run, in the inspector it will show a 5 when you create the Resource. It will load the value you assign it to afterwards. The value will still reset to its default value 0 if you click on the reset button.

@tool
class_name CustomResource
extends Resource

@export var custom_value: int = 5

func _init(p_custom_value: int = 5):
	custom_value = p_custom_value

If it has assigned a 5 then its default value will be 5 and when you create a Resource from it the value will be set to 5. The reset button will reset the value to 5.