help with connecting signals

Godot Version

godot 4.4.1

Question

So what I've made here is a tilemap that fills up with sprites within a specified range. Here is the code:

#main.gd
extends Node2D

@onready var grid = $TileMapLayer

@export var rangeX: int
@export var rangeY: int

func _ready():
	for x in range(rangeX):
		for y in range(rangeY):
			grid.set_cell(Vector2i(x,y), 0, Vector2i(0,0), 1)

#testsprite.gd
extends Sprite2D

signal click

func _on_area_2d_input_event(_viewport, _event, _shape_idx):
	if Input.is_action_just_pressed("click"):
		click.emit()

What I want is for each sprite inside the grid to connect a signal to main so it can tell which one is being clicked on. Is that possible from here, or is my approach completely impossible?

(to be clear, testsprite is not a child of main, it exists inside a scene collection in the tilemaplayer)

Not impossible. I’m doing it :slight_smile:

I’m using a global signal bus. Every time a click happens I send a click event through the bus with the coordinates of the control clicked. I connect to the bus with the class that handles the clicks and do everything I need to when a click occurs.

Setup as an autoload class:

#SignalBus
extends Node

@warning_ignore("unused_signal")
signal clicked_coordinates(coords)

I’m probably not fully understanding, but I don’t think this will work if the sprites don’t exist in the main tree before ready is called. (sorry if wrong)

You are wrong :slight_smile:

It doesn’t matter if the sprites exist in the main tree. The global bus is still available when ready is called, and that is what you are connecting to.

Think about your favorite chat client. It matters not if the friend who wants to talk to you has an account before you set up the client. Since you are getting messages from the chat program all your friend needs to do is send a message to the chat program. It will then get relayed to you.

A global bus works in the same way. It is a persistent communication channel that exists independently from the code that is sending messages through it.

nvm, I now have a working signal bus. However, now I can’t think of how to make each sprite transmit its position in the grid.

I do see how to send variables through signals, but how do I get the info I need?

Get the info from the sprite.

GlobalBus.clicked_coordinates.emit(global_position)

If you are going to send more than one piece of data, I recommend using a resource.

If you are talking about how to get the information that has been sent, you define a method to handle the data and connect it to the signal.

SignalBus.clicked_coordinates.connect(_clicked)
func _clicked(coords) -> void:
    # This is what you do with clicks

I don’t need the global position, I need the tile the sprite occupies in the tilemaplayer.

like this:

nvm again, got it with some conversion shenanigans. Here’s my final code so others can do this:

#main.gd
extends Node2D

@onready var grid = $TileMapLayer

@export var rangeX: int
@export var rangeY: int

func _ready():
	Clacks.click.connect(clicked)
	
	for x in range(rangeX):
		for y in range(rangeY):
			grid.set_cell(Vector2i(x,y), 0, Vector2i(0,0), 1)

func clicked(global_position):
	var local = to_local(global_position)
	var tile = grid.local_to_map(local)
	print(tile)
#clacks.gd (signal bus)
extends Node

@warning_ignore("unused_signal")
signal click()
#testsprite.gd
extends Sprite2D

func _on_area_2d_input_event(_viewport, _event, _shape_idx):
	if Input.is_action_just_pressed("click"):
		Clacks.click.emit(global_position)

Thanks for your help!