Referencing a custom resource returning null

Godot Version

4.4.1

Question

Hi! I’ve set up a custom resource that just contains an array of PackedScenes. I can get it all set up in the inspector nicely and everything looks good.
I want to access this array at runtime and spawn a new scene from the array, but the resource is nil at runtime.

My Custom Resource:

extends Resource
class_name MergeData

@export var merge_progression: Array[PackedScene]

and referencing it in the script:

extends Area2D

@export var merge_data: MergeData #this is assigned in the inspector

func _ready() → void:
if merge_data != null:
print(“merge_data is ready to rock!”)

Unfortunately, merge_data is null.

I notice that if I load the resource directly by the file path, it gets loaded properly:

merge_data = load(“res://Scenes/MD_Generic.tres”)

I suppose I could set the load path for the resource to use in the inspector instead of dragging the resource, but that’s a less convenient workflow. Any suggestions?

am I plagued with bugs today? I’m not sure why my code blocks aren’t rendering correctly T-T
edit: made codeblocks quotes instead because they were appearing empty

What’s it look like in the inspector when you export the variable? Can you show a screenshot?

sure! the images in the array preview are wrong for some reason but they are correct while the game is running

1 Like
extends Area2D

@export var merge_data: MergeData

func _ready() -> void:
	if merge_data.merge_progression != null:
	print(“merge_data is ready to rock!”)

What happens if you run this?

invalid access to property or key 'merge_progression' on a base object of type 'Nil'

That’s helpful. Nil is different from null. null means that the object has no value. Nil means the object doesn’t exist.

What node and its attached code is using your Area2D?

Oh I didn’t realize the difference, thanks.

I’m not sure exactly what you’re asking, sorry.
My code is on an Area2D. I’m making other scenes that use the same script and trying to get these scenes to interact.

What I’m saying is that I don’t have enough information to help you with your problem. Based on what you’re showing me - it should work. So there must be code you are not showing me that’s causing the problem.

2 Likes

After exiting and restarting Godot I’m getting a new error:


If I choose fix dependencies it doesn’t appear that anything is broken?

When I run the game I now see the error:

E 0:00:00:560   _printerr: res://Scenes/House_1.tscn:62 - Parse Error: [ext_resource] referenced non-existent resource at: res://Scenes/MD_House.tres.
  <C++ Source>  scene/resources/resource_format_text.cpp:39 @ _printerr()

and

E 0:00:00:564   _load: Failed loading resource: res://Scenes/MD_House.tres. Make sure resources have been imported by opening the project in the editor at least once.
  <C++ Error>   Condition "found" is true. Returning: Ref<Resource>()
  <C++ Source>  core/io/resource_loader.cpp:343 @ _load()

It’s telling me to “Make sure resources have been imported by opening the project in the editor at least once.”
But now I can’t open any scenes without getting the error:

How do I make sure these resources are properly saved and imported?

This is where the files are saved, if you want to check:

This is the full script I was running - it would fail on ready() which is why I didn’t immediately share the whole thing.

extends Area2D

@export var merge_data: MergeData
@export var merge_level: int
var end_of_merge_tree: bool

var this_prop: RigidBody2D
var other_prop: Area2D

var hovering: bool

func _ready() -> void:
	# getting the resource it this way works:
	# merge_data = load("res://Scenes/MD_Generic.tres")
	
	#get the parent of this prop
	this_prop = get_parent()

	#check if we're at the end of the merge tree
	if merge_data != null:
		if merge_level+1 == merge_data.merge_progression.size():
			end_of_merge_tree = true
	else:
		print("null error: " + this_prop.name)
		#this is showing me that the props this script is running on all have the issue 

	#connect the body_entered signal for merging
	self.connect("area_entered", _on_area_entered)
	self.connect("area_exited", _on_area_exited)

# Called when props with this script attached overlap
#these functions seem to be working fine
func _on_area_entered(body: Node2D):
	if "other_prop" in body: #check if the colliding body has "other_prop", we assume its got this script attached if so.  
		other_prop = body
		if compare_merge_type(body):
			this_prop.debug_text = true
			this_prop.update_debug_label("hovering")
			hovering = true
func _on_area_exited(body: Node2D):
	hovering = false #because this script is on both props, this happens automatically on other prop too
	this_prop.update_debug_label(" ")

func _input(event: InputEvent) -> void:
	if event is InputEventScreenTouch and event.pressed == false: #when touch ends
		if other_prop != null and hovering and other_prop.hovering and !end_of_merge_tree:
				print("WILL MERGE")
				#delete other prop
				other_prop.this_prop.queue_free() 
				#spawn next prop
				#The bug shows up here because merge_data is nil:
				var new_prop = merge_data.merge_progression[merge_level+1].instantiate()
				get_tree().root.add_child(new_prop)
				new_prop.global_position = this_prop.global_position
				print(str(new_prop))
				#delete this prop
				queue_free()

#Helper function to decide if these two can be merged
func compare_merge_type(body: Node2D) -> bool: #this is only going to work if merge data & level is the same
	if !end_of_merge_tree and merge_data == body.merge_data and merge_level == body.merge_level:
		return true
	return false

And the custom resource is only

    extends Resource
    class_name MergeData

    @export var merge_progression: Array[PackedScene]

Ok, well right now you have a problem with GenericProp.tscn. So I suggest you open it up, make sure everything is assigned correctly if there are exported variables and save it. Then reboot your project.

I suspected you were having this happen in _ready() because the issue you are having appears to be a race condition. But I didn’t want to play guessing games.

	# getting the resource it this way works:
	# merge_data = load("res://Scenes/MD_Generic.tres")

This works because you are loading the value fully before using it. This would also work in place of your @export:

@onready var merge_data = load("res://Scenes/MD_Generic.tres")

Although, if MD_Generic.tres doesn’t change during the game, using preload would be more appropriate because it caches it so additional calls to preload don’t load it twice. (For example in another script.):

@onready var merge_data = preload("res://Scenes/MD_Generic.tres")

If using an @export variable is important to you (and that’s fine), you need to give time for the @export variable to actually load - which you are not guaranteed during _ready(). It runs to get everything ready.

So just update your code like this:

func _ready() -> void:
	#get the parent of this prop
	this_prop = get_parent()

	#connect the body_entered signal for merging
	area_entered.connect(_on_area_entered)
	area_exited.connect(_on_area_exited)
	ready.connect(_on_ready)


func _on_ready() -> void:
	#check if we're at the end of the merge tree
	if merge_data != null:
		if merge_level+1 == merge_data.merge_progression.size():
			end_of_merge_tree = true
	else:
		print("null error: " + this_prop.name)
	ready.disconnect(_on_ready)

This connects you to the node’s ready signal - meaning all variables are loaded among other things. Then it triggers your code. This should work. Also note that I changed the way your connections are written to update them to the recommended Godot 4 way. If you like the self dot nomenclature becuase it makes your code clearer to you, you can also use:

self.area_entered.connect(_on_area_entered)

I think there’s something more fundamental that’s broken here. I’ve tried a few of your suggestions and none of them seem to be working.

Most telling is

@onready var merge_data = preload("res://Scenes/MD_Generic.tres")

gives me the error

Could not preload resource file "res://Scenes/MD_Generic.tres".

But if I make a new MergeData resource called MD_Test.tres and try to preload it, I don’t get the error.

However, now when I try to populate the Data with .tscn files, I get the error:

  ERROR: scene/resources/resource_format_text.cpp:282 - Parse Error: Busy. [Resource file res://Scenes/House_1.tscn:62]
  ERROR: Failed loading resource: res://Scenes/House_1.tscn. Make sure resources have been imported by opening the project in the editor at least once.

I wasn’t having these issues with the scene or custom resources yesterday, these issues came up after Godot restarted. Even rolling back on my version control to a point where it was working is now giving me issues saying these resources are missing.

Ok, then it sounds like something got corrupted. My recommendation is you copy your project folder to another location so you have a backup copy. Then you delete every .tres file that you made that’s giving you a problem and remake them from scratch.

I doubt it was the rebooting perse, but if you change the structure of a Resource file after creating resources that rely on it - this can happen.

I also recommend that once you’ve got everything working, you grab the GitHub plugin and learn how to use it so you can back your projects up and go back to an older version if something like this happens again.

1 Like

Thanks for your help, I probably changed something about the resource file at some point - I don’t remember when - while figuring out the structure I wanted for this feature.

I’m using BitBucket and SourceTree for my version control, i didn’t know there was a GitHub plugin for Godot I’ll check it out, thanks!

1 Like

Honestly if you’re using BitBucket and SourceTree and you know them that works too. TBH I just use GitBash and skip the GitHub plugin myself.

1 Like