Best way to expose properties of a secondary class

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By notanome

Hi y’all!

I’m creating an inventory system. There are the ItemData, which are basically static, .tres resources describing a kind of item, like “wheat”, “flour”, etc… With properties like name, value, ingredients used to make it, icon, etc…

And then there are Items themselves - they store a reference to an ItemData and have an amount and are what is actually used in an inventory. This leads to accessing the properties of the data reference when dealing with items:, item.ref.value

And so on. This concerns me because, ideally, external systems should not be worried about the internal makeup of the Item class and the mental hoops to change from ref to item caused some silly mistakes.

I thought of some possible solutions:

  1. Item extends ItemRef, values are copied on _init.
    This works but causes data duplication and sync issues. ie: if the changes, the item would be outdated. This would require updating the item on ref change, which seems… ugh.

  2. Item _get(property) points to ItemRef
    Works but breaks autocompletion and is unsafe, due to the dynamic nature of it

func _get(property: String):
	return ref.get(property)

  1. Item with same fields as ItemRef and a lot of setget
    Works but is quite verbose. I mean:

var my_name : String setget noop, get_my_name

func noop(v):

func get_my_name():
	return ref.my_name

I was wondering if anyone else has a better solution (or maybe even a different idea altogether!).

My mind immediately went to option 3. It encapsulates the implementation details while safely exposing internals in such a way that shouldn’t change during the development process. The only thing to be mindful of is using self.member to access your member variables from inside the script in order to trigger the setters/getters (the need to do this is going away with godot 4).

Option 2 lends itself to spelling errors so that’s immediately out in my mind.

Option 1 really depends on what you plan on doing with it. It can be okay if the ref only stores things like max/min values, static names, description flavor text. It would be a poor choice for things that are constantly being updating e.g. quantity owned, durability. COULD be okay. (I use this when I would rather create a struct in other languages and I don’t want to mock a struct with a dictionary.) I’m not sure if resources will give you auto-completion of member variables from the editor if accessed in this manner. If they don’t you could always create your own class that extends Node then give it class_name ItemData. When create your reference variable in your script you just qualify it: var data : ItemData and it should give you the auto-completion in the editor. I also use this style when I’m referencing something that makes sense in my head like player.CharacterSheet.strength since it immediately makes sense for me in my head to think that a strength variable would be in a player’s CharacterSheet.

timothybrentwood | 2021-08-19 03:31

I agree with your points! I was basically using the player.CharacterSheet.strength style, but I would like external systems not to have to think about refs. It felt weird to type item.ref.value instead of item.value.

Godot 4 would make number 3 much less of a hassle with the new setget API (and typed arrays, for when dealing with many items).

notanome | 2021-08-19 14:20