How to make a signal go through multible scenes

Godot Version

4.5

Question

I am making a cookie clicker type game and i have some shop buttons in a “shop” scene inside of a “gui” scene inside of a “main” scene. Inside of the shop scene i have buttons that i want to send a signal i can connect to the main scene but i am having trouble doing that. Im basicly asking how to connect a signal through 2 or more scenes. If it helps here is my code:

Shop script:

extends Control

signal click_boost_upgrade_pressed(node, cost)
signal idle_clicker_upgrade_pressed(node, cost)

@onready var idle_btn  = get_node("HBoxContainer/idle_clicker_upgrade_button")
@onready var boost_btn = get_node("HBoxContainer/click_boost_upgrade_button")

var idle_cost = 20
var boost_cost = 40

func _ready():
	await get_tree().process_frame
	idle_btn.pressed.connect(_on_idle_pressed)
	boost_btn.pressed.connect(_on_boost_pressed)

func _on_idle_pressed():
	emit_signal("idle_clicker_upgrade_pressed", self, idle_cost)

func _on_boost_pressed():
	emit_signal("click_boost_upgrade_pressed", self, boost_cost)

GUI script:

extends Control

signal click_boost_upgrade_pressed(node, cost)
signal idle_clicker_upgrade_pressed(node, cost)

@onready var shop = get_node("Shop")

func _ready():
	await get_tree().process_frame
	shop.click_boost_upgrade_pressed.connect(func(node, cost): emit_signal("click_boost_upgrade_pressed", node, cost))
	shop.idle_clicker_upgrade_pressed.connect(func(node, cost): emit_signal("idle_clicker_upgrade_pressed", node, cost))

Main script:

extends Control

@onready var coin = get_node("Coin")
@onready var coin_text = get_node("GUI/coin_text")
var coins = 0

var coin_boost = 0;
var coin_boost_multi = 0;

var coin_idle = 0;
var coin_idle_multi = 0;

func _on_coin_coin_clicked():
	coin_add(1 + coin_boost)
	
	var x = randf_range(5, 950)
	var y = randf_range(-20, 440)
	
	while x > 585 or x < 370 and y < 125:
		x = randf_range(5, 950)
		y = randf_range(-20, 440)
	coin.position = Vector2(x, y)


func _on_idle_timer_timeout():
	coin_add(coin_idle)

func coin_add(amount):
	coins += amount
	coin_text.text = "Coins: " + str(coins)


func _on_gui_click_boost_upgrade_pressed(boost_btn, cost):
	coin_boost += 1 * coin_boost_multi
	boost_btn.boost_cost = cost + cost / 2 + cost / 5


func _on_gui_idle_clicker_upgrade_pressed(idle_btn, cost):
	coin_idle += 1 * coin_idle_multi
	idle_btn.idle_cost = cost + cost / 2 + cost / 5

I believe the problem is somewhere in the GUI or Shop script but i just cant fix it.

In the editor, you can only connect inside the currently edited scene. You can’t go “above”. If you want to connect a signal that’s inside one of instantiated scene, you can enable “Editable Children” for the instance (right click on the instance in the inspector)

I am not sure I understand the problem.

Any signal can be collected by as many nodes as you want. You just have to listen for them. If you want your main script and your GUI script to react to the same signal, just connect each script to the signal you want to react to.

For example in your shop script you have:

signal idle_clicker_upgrade_pressed(node_ref: Node, cost: int)

func _on_idle_pressed() -> void:
	idle_clicker_upgrade_pressed.emit(self, idle_cost)

(I have added static typing as best I could guess it and emitted the signal in the new format.)

Then in both your gui and main scripts just listen for it:
Main Script:

func _ready() -> void:
	shop.idle_clicker_upgrade_pressed.connect(_on_idle_clicker_upgrade_pressed)

func _on_idle_clicker_upgrade_pressed(node_ref: Node, cost: int) -> void:
 	# whatever code you need here for your main script to react
	coin_idle += 1 * coin_idle_multi
	idle_btn.idle_cost = cost + cost / 2 + cost / 5

Same in Gui Script:

func _ready():
	shop.idle_clicker_upgrade_pressed.connect(_on_idle_clicker_upgrade_pressed)

func _on_idle_clicker_upgrade_pressed(node_ref: Node, cost: int) -> void:
 	# whatever code you need here for your GUI to react

That is the beauty of signals. You emit a signal and it can be listened for anywhere and in multiple places at once. I would suggest re-reading the docs on signals. It is very common to listen for signals in multiple places, like a signal ‘player_died’ might be listened for by the UI, the camera, the game manager, all the enemies, all the friendly units and even the home base etc.

Have you ever heard of using a signal manager if you are indeed instantiating nodes and having problems connecting signals because the nodes don’t exist at run time to easily connect to them. I can tell you a little about this if this is the source of the problem? Or you can google it. They are often called signal buses if you do google it.

Anyway, in short, in whatever script you want to listen for a signal in, just connect to the signal as you have been doing, and you can do this in as many nodes as you want to, listening to the same signal so multiple nodes can react.

I hope that helps.

3 Likes

Thank you, this isn’t the exact solution i was looking for but i appreciate the helpful response. It will definitely help me in the future.

The signal manager/ signal bus method the last person mentioned is likely what you need. You’ll be making an auto load script that all scenes can reference and connect to. So you can trigger a signal from your store/shop scene in your signal bus that’s connected to as many other scenes as you need to.

It’s relatively easy to setup and really helps keeping things tidy in your scripts.

1 Like

The drawback is that it globalizes your signals. As in general with variables, the number of global signals should be kept as low as possible, to avoid the spaghettization of your architecture.

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