Dynamically change which property a script accesses

Godot Version

4.5.1.stable

Question

I’m trying to make a function that dynamically changes the properties of a node (e.g: x position, y scale, variables, etc).

I understand that you can use .get() and .set() to dynamically get / set variables in a node, however, is there an alternative for built-in properties (scale, position, visibility, etc)?

Thanks!

EDIT: I don’t think my original post explained it well enough. In a nutshell, this is what I’m trying to achieve:

var axis : string = “x”

$ExampleNode.position.[axis] = 2.0

Basically: I want to be able to change “axis” to any axis: “x”, “y”, “z”, so that the 2nd line automatically updates to get: “position.x”, “position.y”, etc etc. Hope that explains it

You can use get and set for built in properties as well.

If you are working typing variables, like var some_node : Node3d instead of var something, when editing the script and type some_node and dot, the list of properties and methods will be displayed.
For creating your own getters and setters, you can expose a variable name and through the setter and getter set and get the values too. This way, you can add logic or validations.
Example:

var _input_dir : Vector2 = Vector2.ZERO
var _jump : bool = false
var _crouch : bool = false
var _run : bool = false
var _interact : bool = false

var input_dir : Vector2 :
get():
return _input_dir

var jump : bool :
get():
return _jump

var crouch : bool :
get():
return _crouch

var run : bool :
get():
return _run

var interact : bool :
get():
return _interact

add get in from of the property to use the method:


global_position #**property**
get_global_position() #**method** of the property, returns Vector2
set_global_position(Vector2(1, 0)) #**method** of the property, sets Vector2

you can find these in the documentation or by Ctrl+Clicking on the property.

every single property in the engine has a get and set methods.

What exactly do you mean by “dynamically change”? Do you mean “while the game is running”? If so, they’re properties, just change them with an assignment.

position.x = 1.0
scale.y = 0.5

Using getters and setters is just more verbose code.

If you’re asking how you do that - use a script. If you want a more specific answer, you’ll have to give a more concrete example.

Sorry for the late reply.

I don’t think my original post explained it well enough. In a nutshell, this is what I’m trying to achieve:

var axis : string = “x”

$ExampleNode.position.[axis] = 2.0

Basically: I want to be able to change “axis” to any axis: “x”, “y”, “z”, so that the 2nd line automatically updates to get: “position.x”, “position.y”, etc etc. Hope that explains it

I would make this by using an enum. The code might not be the shortest possible, but at least it would be clear.

enum Axis {X, Y, Z}

var axis = Axis.X

...

if axis == Axis.X:
    $ExampleNode.position.x = 2.0
elif axis == Axis.Y:
    $ExampleNode.position.y = 2.0
else:
    $ExampleNode.position.z = 2.0

this way:

print(self[property])

I’d recommend @Dizzy_Caterpillar’s Enum suggestion, but I would go with:

extends Node3D

enum Axis {X, Y, Z}

var my_axis: Axis = Axis.X


func _ready() -> void:
	print(position[my_axis])

Alternately, if you fix @NOTIdealDev’s untested, LLM-hallucinated answer, you can do this:

extends Node3D

var axis: String = "x"

func _ready() -> void:
	print(position[axis])

The first option is more code, but you won’t have runtime errors because your String isn’t sanitized. The latter will work if you make sure nothing other than “x”, “y” or “z” is assigned to it. You can get really fancy and use a Variant, allowing you to take in a String and return a float.

extends Node3D

var axis: Variant = "x":
	set(value):
		if value != "x" or value != "y" or value != "z":
			axis = "x"
	get():
		return position[axis]


func _ready() -> void:
	print(axis)

Edit:

Adding in @gertkeno’s answer:

extends Node3D

var axis: String = "x"

func _ready() -> void:
	print(get_indexed("position:" + axis))

that’s not LLM halucination, i tested it in godot and it works, there is self keyword!

Except in this case OP wanted a part of the property, or a property of a property. So while your code example could in theory work if property was set to something useful, it will not actually solve the problem presented. And therefore looks like an LLM answer.

Switching is really not needed for the vector. The class overloads square brackets to access components by index:

enum Axis {X = 0, Y, Z}
var axis = Axis.Z

func _ready():
	var v = Vector3(1.0, 2.0, 3.0)
	print(v[axis])

Thanks @normalized I forgot about that. I updated my example.

Thanks everyone, really appreciate the help.

set_indexed and get_indexed

var axis: String = "x"

$ExampleNode.set_indexed("position:" + axis, 2.0)

_indexed uses NodePaths which can target deeper properties with : so "position:x" is the same as position.x