Godot Version
Godot 4.4
Question
At first I tried using ResourceSaver to store my inventory data and it’s work perfectly fine for me but later I discover that using Resource to saving data is not safe. So, I started learning JSON instead.
I have no problem with using JSON but I completely have no idea how to using it to imprement inventory system and store inventory data like item and item quantity. I thought it would be simple enough to imprement because it only just a basic grid inventory system but it been a 6 hours at this point and I still have no idea.
If you have any advise on how you imprement it, please let me know.
Also, I watched this tutorial on how to imprement inventory system.
JSON can store most of the same data as Resources, you may have to use paths instead of loading textures directly, i.e. "res://apple_texture.png"
instead of saving the texture itself.
What part of converting to JSON is giving you trouble? Can you share some code?
I basically tried to change from Resource to JSON. When I want to save my Inventory Data, I just simply used ResourceSaver to save, but now I think I might have to change the whole thing in order to save inv data into JSON save file.
item_data.gd
class_name ItemData
extends Resource
@export_category("Item")
@export var name: String
@export_multiline var description: String
@export var texture: Texture
slot_data.gd
class_name SlotData
extends Resource
@export var item_data: ItemData
inventory_data.gd
class_name InventoryData
extends Resource
signal InventoryUpdated(inventory_data: InventoryData)
signal InventoryInteracted(inventory_data: InventoryData, index: int, button: int)
@export var slot_datas: Array[SlotData]
func selected_slot_data(index: int) -> SlotData:
var slot_data: SlotData = slot_datas[index]
if slot_data:
return slot_data
else:
return null
InventoryUpdated.emit(self)
func use_slot_data(index: int) -> void:
var slot_data: SlotData = slot_datas[index]
if not slot_data:
return
if slot_data.item_data is KeyItemData:
if slot_data.item_data.is_one_time_used:
if slot_data.item_data.is_key_item_valid:
slot_data.item_data = null
if !slot_data.item_data:
slot_datas[index] = null
InventoryUpdated.emit(self)
func pick_up_slot_data(slot_data: SlotData) -> bool:
for index in slot_datas.size():
if not slot_datas[index]:
slot_datas[index] = slot_data
InventoryUpdated.emit(self)
return true
return false
func on_slot_clicked(index: int, button: int) -> void:
InventoryInteracted.emit(self, index, button)
I’m using JSON for some things in my project, but only on disk. When I load, I pass it through JSON.parse()
, which gives me a nested structure of Dictionary
and Array
:
# file contents
{
"name": "cheddar",
"type": "cheese",
"description":
[
"You see a sharp cheddar here, quietly calling out for the companionship of an apple.",
"You are beginning to get hungry."
]
"recipes":
{
"mac & cheese": 14,
"ham sandwich": 31,
"omlette": 24
}
}
Once loaded, in Godot I can do something like:
match(object["type"]):
"cheese":
for desc: String in object["description"]:
text.append(_bbcode_paragraph(desc))
if object["recipes"].has(requested_recipe):
print("Aw Yiss %s!11!1!!1 page %d" % [requested_recipe, object["recipes"][requested_recipe]]
You may have to change how you save the texture
, since it’s not a simple type. Again I recommend saving the resource path. Using a different data type will mean changing some or most of your save code.
class_name ItemData
extends Resource
@export_category("Item")
@export var name: String
@export_multiline var description: String
@export var texture: Texture
func create_save_string() -> String:
return JSON.stringify({
"name": name,
"description": description,
"texture": texture.resource_path,
})
func read_save_string(data: String) -> void:
var json := JSON.parse_string(data)
if json:
name = json.data["name"]
description = json.data["description"]
texture = load(json.data["texture"])
else:
push_warning("Couldn't read json: ", data)
Thanks for you guys reply. I think I know my problem now.
inventory_data.gd
class_name InventoryData
extends Node
@export var item_datas: Array[ItemData]
func save_item_datas() -> Dictionary:
var datas: Dictionary
for items in item_datas:
if items:
datas[items.name] = items.resource_path
return datas
func load_item_datas(data) -> void:
item_datas.clear()
var item_counter: int = 0
for items in data.inventory_data:
item_datas.append(load(data.inventory_data.values()[item_counter]))
item_counter += 1
print(item_datas)
I forgot that inventory supposed to be dynamic and I should save item in inventory individually, but I instead tried to save the whole inventory_data because it’s a resource. I think that is my problem.
Yeah if the items themselves are well defined in resource files then you can create an array of the paths and save that as a JSON string.
Using a Dictionary for datas
means you cannot have two of the same item (by .name
); I don’t know if that’s intentional as it’s different from the item_datas
as an Array which can store duplicates.
Here’s an example of how I would read and write an array of strings in JSON.
class_name InventoryData
extends Node
@export var item_datas: Array[ItemData]
func create_save_string() -> String:
var items_paths: Array[String] = []
for item in item_datas:
items_paths.append(item.resource_path)
return JSON.stringify(items_paths)
func read_save_string(data: String) -> void:
var json := JSON.parse_string(data)
if json is Array:
for path: String in json:
item_datas.append(load(path))
else:
push_warning("Couldn't read json: ", data)
Yes, you’re right. My head is a little bit messy when I wrote those logic lol. Now, I think I can figure out the rest of this system. Thanks again for your advise.