When using ResourceLoader I get this error message. Does anyone know what it means and what to do to fix it? I'm loading back a saved resource and this message pops up several times during that process.

E 0:00:01.193   _create_instance: Condition "r_error.error != Variant::CallError::CALL_OK" is true. Returned: __null
  <C++ Source>  modules/gdscript/gdscript.cpp:121 @ _create_instance()
  <Stack Trace> SaveManager.gd:22 @ load()
                PlayerData.gd:263 @ pack()

The data and nested resources in the saved resource are not loaded back properly so I guess it has something to do with that.

Whats in PlayerData.gd at & around line 263 and in SaveManager.gd at & around line 22?

@Megalomaniak said: Whats in PlayerData.gd at & around line 263 and in SaveManager.gd at & around line 22?

PlayerData's pack() function adds all the PlayerData variables to a dictionary (the data variable) which I then send to the SaveManager's save() function (SaveManager is an autoload with name Save). In PlayerData around line 263 is:

Save.save("PlayerData", data)
var load_data = Save.load("PlayerData")
print (load_data.equipped_amulet.item_name)

At the moment I'm saving and then directly loading for debugging. var load_data = Save.load("PlayerData") is line 263

The SaveManager:


    var save_data = SaveResource.new()
    
    func save(context : String, data : Dictionary):
    	save_data.version = ProjectSettings.get_setting("application/config/version")
    	save_data.data[context] = data
    	var error : int = ResourceSaver.save("user://save_game.tres", save_data)
    	if error != OK:
    		push_error("There was an error when saving: %s" % error)
    	print("SAVED")
    
    func load(context : String):
    	var load_data := ResourceLoader.load("user://save_game.tres", "", true)
    	print ("LOADED")
    	print(load_data.data[context].equipped_items_inventory)
    	return load_data.data[context]

var load_data := ResourceLoader.load("user://save_game.tres", "", true) is the SaveManager's line 22

And the SaveResource:

    extends Resource
    class_name SaveResource

    export var version : String
    export var data : Dictionary

In the example from PlayerData, the last print should give me the name of the equipped amulet but the Resources that gets loaded back is empty.

This is the .tres data that gets saved. Not sure how these are supposed to look:

[gd_resource type="Resource" load_steps=14 format=2]

[ext_resource path="res://Inventory/Inventory.gd" type="Script" id=1]
[ext_resource path="res://SimItem.gd" type="Script" id=2]
[ext_resource path="res://SaveResource.gd" type="Script" id=3]
[ext_resource path="res://Weapons/Weapon.gd" type="Script" id=4]
[ext_resource path="res://Shields/Shield.gd" type="Script" id=5]

[sub_resource type="Resource" id=1]
script = ExtResource( 4 )
item_name = "Simple Sword"
description = "This is a sword"
item_type = 0
rarity = 4
equippable = true
buy_price = 0.0
sell_price = 0.0
icon_texture = "res://Images/Items/Weapons/Sword01.png"

[sub_resource type="Resource" id=2]
script = ExtResource( 5 )
item_name = "Large wooden shield"
description = "More protection."
item_type = 1
rarity = 4
equippable = true
buy_price = 0.0
sell_price = 0.0
icon_texture = "res://Images/Items/Weapons/Shield_02_BigIcon_A.png"

[sub_resource type="Resource" id=3]
script = ExtResource( 2 )
item_name = "Null item 1645"
description = "This is a temp item for debugging and testing"
item_type = 2
rarity = 0
equippable = true
buy_price = 0.0
sell_price = 0.0
icon_texture = "icon.png"

[sub_resource type="Resource" id=4]
script = ExtResource( 2 )
item_name = "Null item 1646"
description = "This is a temp item for debugging and testing"
item_type = 3
rarity = 0
equippable = true
buy_price = 0.0
sell_price = 0.0
icon_texture = "icon.png"

[sub_resource type="Resource" id=5]
script = ExtResource( 2 )
item_name = "Null item 1647"
description = "This is a temp item for debugging and testing"
item_type = 4
rarity = 0
equippable = true
buy_price = 0.0
sell_price = 0.0
icon_texture = "icon.png"

[sub_resource type="Resource" id=6]
script = ExtResource( 2 )
item_name = "Null item 1648"
description = "This is a temp item for debugging and testing"
item_type = 5
rarity = 0
equippable = true
buy_price = 0.0
sell_price = 0.0
icon_texture = "icon.png"

[sub_resource type="Resource" id=7]
script = ExtResource( 1 )

[sub_resource type="Resource" id=8]
script = ExtResource( 1 )

[resource]
script = ExtResource( 3 )
version = "0.1"
data = {
"PlayerData": {
"base_damage": 18.0,
"credits": 0,
"defense": 2.0,
"equipped_amulet": SubResource( 6 ),
"equipped_armour": SubResource( 4 ),
"equipped_headgear": SubResource( 3 ),
"equipped_items_inventory": SubResource( 7 ),
"equipped_ring": SubResource( 5 ),
"equipped_shield": SubResource( 2 ),
"equipped_weapon": SubResource( 1 ),
"health": 20,
"items_inventory": SubResource( 8 ),
"level": 1,
"mana": 20.0,
"max_damage": 19.0,
"max_health": 20,
"max_mana": 20,
"min_damage": 17.0,
"regen": 0.1,
"xp": 8
}
}

Well, I do not know why the amulet is failing to be instanced/created but it seems you are on the right track in terms of finding the source of the error in OP.

I think I have narrowed down what the problem is, but I have no idea how to solve it. Seems like a bug. If the resource is created with parameters in the constructor, or the _init() function, the ResourceLoader won't load it. So in my example above all the items are created from code, like Weapon.new(player_level, spawner_object), which will cast the error.

Hmm, strange. I might suggest making a bug report on the Godot GitHub repository about the issue, if none already exist.

For working around it, could you make a setup function? It would be a tad cumbersome, but then you might be able to work around the issue. Something like this, for example:

# Setup function in class (just an example)
func setup(p_arg_1 : String, p_arg_2 : float):
	self.string_variable = p_arg_1
	self.float_variable = p_arg_2

# Then in the code that spawns it:
var weapon_clone = Weapon.new()
weapon_clone.setup("Test", 10)

Though as I mentioned, it is a bit more cumbersome as you have to define and always call the function after initializing/creating instead of just passing the variables in the initializer.

@TwistedTwigleg said: Hmm, strange. I might suggest making a bug report on the Godot GitHub repository about the issue, if none already exist. Very strange! ResourceSaver/Loader is such a great tool otherwise. It seems like a weird limitation to not handle constructors. I've added a bug report.

For working around it, could you make a setup function? Yes, I think that is my only option at the moment. Thanks!

Ok, just if someone stumbles upon this thread in the future this is what is going on. As explained in the comments to the bug report I made this is not a bug but rather inconsistent results of the export keyword and required default values in the constructor (and bad error messages).

The correct way to set up resources to be loaded by ResourceLoader is:

extends Resource
class_name ResourceClass

export var variable_to_export : String

func _init (_value : String = "")
    variable_to_export = _value

You need to predefine a value for each variable in the constructor and you need the export keyword for each variable you want to save. Then you can do this and it will work just fine :) :

var resource_to_save = ResourceClass.new("A new value")

ResourceSaver.save("path.tres", resource_to_save)
var loaded_resource = ResourceLoader.load("path.tres")
print(loaded_resource.variable_to_export)

Should print "A new value"

a year later