Can a button in one scene trigger a method in a different scene?

Godot Version

4.4.1.stable

Question

Is it possible for a button in one scene to trigger a method in a different scene? If so, how would I do that? The reason for this is because when I press the button in the 1st scene, I want it to spawn a sprite in a 2nd scene.

The button’s signal is shown in the node “1” below:

The method I want triggered is in node “2” below:

If they ever exist inside the same scene tree then yes, for example if you instantiate this Loading_cargo scene into your World_map.

otherwise you will have to store this button pressed information elsewhere.

If the function is in a class then you can just call the class.functuon()

You can use a signal bus to reroute your signal to any node.

Basically, create an auto load scrip named signal_bus.gd:

# signal_bus.gd
class_name SignalBus
extends Node

signal cargo_sent(any_needed_info)

And in where you want to send the signal:

func _on_send_cargo_pressed() -> void:
	SignalBus.cargo_sent.emit(any_needed_info)

And in where you want to receive the signal:

func _ready() -> void:
	SignalBus.cargo_sent.connect(your_method)
2 Likes

Ok, so I’m trying your solution. I made a “signal_bus.gd” script and put the following code in there:

class_name SignalBus
extends Node2D

signal cargo_sent()

I have the following code in the “Cargo” TileMapLayer under the “Loading_cargo” node (this is in a different scene):

extends TileMapLayer

signal cargo_sent()

func _on_send_cargo_pressed():
	signal_bus.cargo_sent.emit()

I have the following code in the “TileMapLayer” node under the “World_map” node:

func _ready():
	signal_bus.cargo_sent.connect(airship_transit())

However, I am getting the ‘Cannot connect to the ‘cargo_sent’: the provided callable is null’ error.

you are connecting the returned value of your method to your signal.
Use your method name , without the parentheses.
Try:

func _ready(): 
	SignalBus.cargo_sent.connect(airship_transit) # use SignalBus, and remove the parentheses of airship_transit.

Also, in your signal_bus.gd, you use the class_name SignalBus, so use SignalBus, the class name, to access anything inside.

Also, your are emitting a signal in SignalBus, so you don’t need a signal in you “Loading_cargo” script. So

extends TileMapLayer

# signal cargo_sent()  This line can be removed

func _on_send_cargo_pressed():
	SignalBus.cargo_sent.emit() # use SignalBus, instead of signal_bus

Also, make sure you signal_bus.gd script is auto loaded, so it can be access everywhere. Doc is here: Singletons (Autoload).

2 Likes

Understood. I tried your method, below is the code I have so far:

This is the updated code for the ‘TileMapLayer’ node in the World_Map scene:

func _ready():
	populate_cities()
	SignalBus.cargo_sent.connect(airship_transit)

This is the updated code for the ‘cargo’ node in the Loading_cargo scene:

func _on_send_cargo_pressed():
	SignalBus.cargo_sent.emit()

Below is the code I have for the signal_bus.gd:

# signal_bus.gd
class_name SignalBus
extends Node

signal cargo_sent()

I do have the signal_bus autoloaded:
image

Despite all this, I am getting the "Cannot find member “cargo_sent” in base “SignalBus” error. I am unsure why “cargo_sent” is not recognized, despite having it in the signal_bus script.

Ok, my mistake.

Once you autoload a script, you don’t need a class_name. You can use the name when you set the autoload scrip. In your case, “signal_bus”.

The class_name SignalBus is not needed in a autoload script.

# signal_bus.gd
# class_name SignalBus   #This line should be removed
extends Node

signal cargo_sent()

And when you need to access your autoload script, use “signal_bus”. This is the name you fill into the Node Name section when you add your script .

I suggest you can just change you name from “signal_bus” to “SignalBus” in the autoload setting. That should fix everything, and you can continue using SignalBus to access your script everywhere.
SignalBus

If you still want to use the name “signal_bus” in autoload setting , then use signal_bus in you code:

func _ready():
	populate_cities()
	signal_bus.cargo_sent.connect(airship_transit) # signal_bus instead of SignalBus
func _on_send_cargo_pressed():
	signal_bus.cargo_sent.emit() # signal_bus instead of SignalBus

I misremembered how autoload work, class_name is not needed, sorry about that.

1 Like

No worries, I appreciate your feedback. That fix did allow the game to run now, so it worked out. However, for the airship_transit method, I have the following code so far:

func airship_transit():
	print("1")

When I test the signal, I don’t see the “1” in the output section of Godot. The signal originates from the following:

func _on_send_cargo_pressed():
	income_made.text = "$ " + str(cargo_income)
	signal_map.cargo_sent.emit()

The ‘income_made.text’ does get updated on the screen, so I know the ‘_on_send_cargo_pressed()’ method is working as intended. The signal should be sent. But I don’t see the ‘airship_transit’ output for some reason.

This seems to be an unusual case then.

Things you need to check:

  1. Check if airship_transit method lives in a script that binds to an existing node on the scene tree when you press the send_cargo button.

  2. Check order of execution. Dose the _ready function connecting the signal called after the signal is sent. You can add a print function in _ready to check.

Did some debugging and as it turns out, the _ready function connecting the signal does not get triggered once the “_on_send_cargo_pressed()” is triggered. Why is the _ready() method not being activated and how do I fix this?

FYI, the “airship_transit()” method is on the ‘TileMapLayer’ script under the World_Map scene. The “_on_send_cargo_pressed()” is in a script for a tilemaplayer named “Cargo” in the 'loading_cargo" scene.

The only reason I can possibly imagine is that you create the node, whose script containing airship_transit(), after send_cargo button press.

_ready function triggers when a node enters scene tree. If your node is created at runtime(not already in the scene tree before you run your game, usually by instantiate a PackedScene), then the _ready function of this node will only be called after you add it to the scene tree with ParentNode.add_child. Check the doc: _ready vs. _enter_tree vs. NOTIFICATION_PARENTED.

So, if you are creating you node on the fly, then _ready happens after add_child.

I was able to figure it out. I didn’t include the following line of code:

	get_tree().change_scene_to_file("res://scenes/World_Map.tscn")

I added this line on the “_on_send_cargo_pressed()” method. THEN it worked out fine. Whoops.

Thank you for your help, I really do appreciate it.

1 Like

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