I know this is probably stupid simple, but I’m a beginner and can’t get a straight answer anywhere. All I want to know is how you pass down a variable from one node to a grandchild node, with the entire process being confined within the scene. If there’s multiple ways, I’d like to know them too.
edit: I should probably clarify further. There is a root node in a scene with a child, and that child node also has a child node. A variable is going to be updated every frame in the root node, and the grandchild node needs to know every frame what that variable currently is. The entire scene is completely static and there will only ever be one of it.
There’s lots of ways. While @fireside’s option will work, it’s not recommended because you’re hard-coding a node path. Instead you should use an @onready variable. (See below.)
Let’s say you have 3 nodes:
NodeA
|____NodeB
|____NodeC
let’s say that they all have a variable named foo.
Use an @onready Variable
# node_a.gd
extends Node
var foo: int = 1
@onready var node_c: Node = "$NodeB/NodeC"
func pass_down() -> void:
node_c.foo = foo
Use an @onready Variable with a Unique Name
Right-click on NodeC in the Scene Tree and select % Access as Unique Name.
# node_a.gd
extends Node
var foo: int = 1
@onready var node_c: Node = "%NodeC"
func pass_down() -> void:
node_c.foo = foo
Use a Signal
# node_a.gd
extends Node
signal change_foo(value: int)
var foo: int = 1
func pass_down() -> void:
change_foo.emit(foo)
# node_c.gd
extends Node
var foo: int = 3
@onready var node_a: Node = "$../NodeA"
func _ready() -> void:
node_a.change_foo.connect(_on_change_foo)
func _on_change_foo(value: int) -> void:
foo = value
Use a Signal with a Unique Name
Right-click on NodeA in the Scene Tree and select % Access as Unique Name.
# node_a.gd
extends Node
signal change_foo(value: int)
var foo: int = 1
func pass_down() -> void:
change_foo.emit(foo)
# node_c.gd
extends Node
var foo: int = 3
@onready var node_a: Node = "%NodeA"
func _ready() -> void:
node_a.change_foo.connect(_on_change_foo)
func _on_change_foo(value: int) -> void:
foo = value
Use a Signal Bus
Create a new scene.
Add a node to it.
Rename the node SignalBus. (Not strictly necessary but helps with clarity.)
Save the scene as signal_bus.tscn.
Attach a script to it. (See below for contents.)
Save the script.
Add the scene as an Autoload named SignalBus (not the script).
# signal_bus.gd
extends Node
@warning_ignore("unused_signal")
signal change_foo(value: int)
# node_a.gd
extends Node
var foo: int = 1
func pass_down() -> void:
SignalBus.change_foo.emit(foo)
# node_c.gd
extends Node
var foo: int = 3
func _ready() -> void:
SignalBus.change_foo.connect(_on_change_foo)
func _on_change_foo(value: int) -> void:
foo = value
If your scene tree is static (the number and position of grandchildren don’t change), you can also use
self.get_child(temp1).get_child(temp2).foo =
# temp 1 and 2 should be an int value or an int var
# temp1 is the child node and temp2 is the grandchild, these values are determined by node order in the scene tree, node orders are indexed (not counted), so the first child in order should be 0
You can set the variable directly this way without a signal
That’s not how get_child() works. It uses indexes. Which means you have to hardcode it.
Here’s a few more possible ways.
Use get_child()
# node_a.gd
extends Node
var foo: int = 1
func pass_down() -> void:
get_child(0).get_child(0).foo = foo
Use get_children(), set(), and Node Name
# node_a.gd
extends Node
var foo: int = 1:
set(value):
foo = value
pass_down(self)
func pass_down(node: Node) -> void:
for node: Node in get_children():
if node.name == "NodeC":
node.foo = foo
pass_down(node)
Use get_children(), set(), and class_name
# node_a.gd
extends Node
var foo: int = 1:
set(value):
foo = value
pass_down(self)
func pass_down(node: Node) -> void:
for node: Node in get_children():
if node is NodeC:
node.foo = foo
pass_down(node)
# node_c.gd
class_name NodeC extends Node
var foo: int = 3
Respectfully, this is 100% congruent with my post, you said I did it wrong, but besides the placeholder temp1 and temp2, this is the same as what I suggested.
It is worth noting a technicality here.
None of the examples given actually “pass a variable down”. They all pass a value down that is then set in a variable that must already exist in the child script.
This is because GDScript passes by value and not reference.
So if we use dragonforges first method as an example:
extends Node
var foo: int = 1
@onready var node_c: Node = "$NodeB/NodeC"
func pass_down() -> void:
node_c.foo = foo
and inside NodeC script we change the value of foo to 12, in the parent script foo will retain the original value of 1.