Export a variable reference?

Godot Version

4.4.1

Question

Hi there,
I’d like to create a generic UI control that could be used to modify any variable in the game (containing for example a PLUS and MINUS buttons, and a label to display the value of the variable). I’d like to add some of these UI controls on a UI panel, manually thru the editor. But I wonder how I can tied one of these control instance to a particular variable (like player.health, or game.score, or Global.any_number, etc…).
Is there a way to @export a reference to a specific variable ? (the same way we can export a reference to a node)

Hi,

To my knowledge, there is no way of referencing a variable in the editor.


In the game I’m currently working on, I’m using a generic slider to display some of the player statistics (mostly used for health and armor). To identify my stats, I’m using an enum which looks like this (I’m working in C# but the idea in GDScript would be exactly the same):

public enum StatType
{
    HEALTH,
    ARMOR,
}

Then, in my player class, I have a dictionary containing the stat instances, with the type as key:

Dictionary<StatType, Stat> statistics;

Which allow me to access those stats based on an enum value that can be exported.
So, in my generic slider class, I can do something like this:

class StatSlider : Control
{
    [Export] StatType statType;  // -> stat to display
    [Export] Player player;      // -> player to get stat from

    void _Ready()
    {
        Stat stat = player.statistics[statType]  // -> get the stat on ready
        // ...
    }
}

However, that would cover only one type of object: player statistics. If you want to be able to display anything from any class, I believe you’ll have to use multiple classes for each of your variable types. Sounds like a bore, but I don’t see any other easy way of doing that.

In Unity, you could use ScriptableObjects to create assets storing variables, and then referencing those assets instead of simple values. Basically, they would act as “variable containers” that do nothing special, but with the advantage of being able to be referenced in exported fields.

Anyway, let me know if that helps.

1 Like

thanks for your help.
For the moment, I am also using an enum, but in my case, I cannot use a linked Array as you do. So I have 2 tedious read/write functions to exchange between the Control and the connected variable…

Could you share some code so that we can see if there’s some easy improvements?

You can use Object.get() and Object.set() to get and set any property of the object using a string.

Something like (I did not test this code):

extends Control
            
            
@export var target:Node
@export var target_property:String = ""


var value:int:
    get():
        var v = -1
        # get the value from target if available
        if target and not target_property.is_empty():
            v = target.get(target_property)
        
        # Await ready if node isn't ready to update Label safetly
        if not is_node_ready():
            await ready

        $Label.text = str(v)
        
    set(v):
        # Set the value to the target property
        if target and not target_property.is_empty():
            target.set(target_property, v)
            
            
        
        
func _on_add_pressed():
    value += 1
    
    

func _on_subtract_pressed():
    value -= 1
2 Likes

Thanks : that is exactly the trick I was looking for !
I did not know about set/get with a string.
I adapted it to my specific problem and it works like a breeze.

BTW, do you know a way to retrieve the type of the property (int / float / bool) ?

You could use @GlobalScope.typeof() to get the runtime type of the variable like:

var value = target.get(target_property)
match typeof(value):
    TYPE_INT:
        print("I'm an int")
    TYPE_FLOAT:
        print("I'm a String")
    TYPE_BOOL:
        print("I'm a bool")
    _:
        print("I'm something else")
1 Like

Like in C++, I should have guess that :laughing: