Send export variable value from parent/root node to child node

Godot Version

v4.2.2.stable.official [15073afe3]

Question

Hi all,
Recently, I wanted to add parameters to these combat encounter scenes I created. I want to edit values in the editor for spawn timer, encounter duration, etc. So the encounter scene has a child node that handles the details of setting up/finalizing the encounter, and I just want to make sure I am passing data from the parent to the child node correctly.

I thought I was seeing weird behavior, which lead to me reading a lot about node lifetime cycles. I realized I was trying a setget in the parent before checking if the child node was in the scene. More technical details here.

So I want to do a one-time transfer of simple data values from parent to child. Is there anything wrong with just using the _ready function on the parent node, like this?

extends Node3D

@export var enemy_spawn_markers: Array[Marker3D]
@export var spawn_delay: float
@export var encounter_duration: float

func _ready():
	$EncounterHandler.enemy_spawn_markers = enemy_spawn_markers
	$EncounterHandler.spawn_delay = spawn_delay
	$EncounterHandler.encounter_duration = encounter_duration

If I can clarify anything please let me know, and thanks for any insight you can provide!

Looks like a simple IOC (inversion of control), nothing wrong with that, simple and no child to parent deps.

IMHO

1 Like

I have a couple more questions, if you don’t mind, because I have done some more tinkering.

I really only need to use the $ symbol once, because that gets a reference to the node. So why use $ for get_node() several times if you don’t need to, right?

Also, since these values correspond to setting child node values, I have to keep in mind that child nodes are created and added to the tree first. So I figured I would set it up like this instead. Any thoughts?

extends Node3D
@onready var encounter_handler: EncounterHandler = $EncounterHandler
@export var enemy_spawn_markers: Array[Marker3D]
@export var spawn_delay: float
@export var encounter_duration: float

func _ready(): 
	encounter_handler.spawn_delay = spawn_delay
	encounter_handler.encounter_duration = encounter_duration
	encounter_handler.enemy_spawn_markers = enemy_spawn_markers
	encounter_handler.configure_encounter()

My main concern is that I want to make sure I understand the order in which nodes are added to the scene.

And by the way, this is what the child node configure_counter() function looks like:

func configure_encounter():
	encounter_spawn_duration_timer.wait_time = encounter_duration
	spawn_interval_timer.wait_time = spawn_delay
	enemy_spawn_markers = enemy_spawn_markers

I really only need to use the $ symbol once, because that gets a reference to the node. So why use $ for get_node() several times if you don’t need to, right?

Yeah it’s sugar and might have performance benefits(it’s a reference, fast access), I would have to look at the C++.

Calling a method once and caching the value is basically what you are doing.

I use C# a lot and I have some different design patterns, so I pre-cache GetNode() once in _Ready().

This is where code is elegant, there are many ways to paint a picture depending on how the whole setup works. Keeping it consistent is the main part.

Also, since these values correspond to setting child node values, I have to keep in mind that child nodes are created and added to the tree first. So I figured I would set it up like this instead. Any thoughts?

It works, as I said I use _Ready() as sort of a IOC constructor to keep my own “notes” of my design, others wouldn’t do this. But it’s just a couple calls to cache the references to the nodes, nothing burger IMO. :slight_smile:

My main concern is that I want to make sure I understand the order in which nodes are added to the scene.

This is kind of why I do what I do in C#, for myself it encapsulates everything in one place, again it’s all about how you design your flow.

1 Like

Great! I feel good about continuing onto other things now. Thank you for the reply!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.