Help with understanding signals

Godot Version

godot 4

Question

I’m new to godot and still a bit confused with signals, I need to connect a signal that I created on one node in one scene tree to another node that is in a different scene tree. Can this be done with signals? (also without using instancing) if not, can someone please please help me figure out a way to achieve the same result using some other method

thanks

For this, you need to access the node that you want to connect somehow. Then you can do like this:

my_node.custom_signal.connect(my_function)

This is just the structure.

1 Like

yes I know that, but the question is how to get the node?? I don’t want to instance it, and it’s not in the same scene tree as the receiver of the signal.

1 Like

But you will have only the one scene (the current), you can’t access any other scene unless you change it. So you need to instance that.

But can you explain exactly what you want, so I can think about the solution.

Forget that other message, the instancing thing still doesn’t work. Basically what I’m doing is I have a main scene, in it there is a spawner node that spawns a bunch of rocks, each one has durability (how many hits it takes to destroy a rock) initially set to 4. Now I have a timer as well in the main scene. Once the timer finishes it changes the scene to a shop scene. In the shop scene I have a button that when pressed SHOULD change the rock durability to one less each time it is pressed (from 4 to 3, from 3 to 2 etc) however, I have no idea how to do this, I thought by just creating an instance of the rock:

var ROCKSCRIPT = preload("res://scenes/rock.tscn")
var rockitem = ROCKSCRIPT.instantiate()

func _on_pressed() -> void:
	rockitem.durability -= 1
	print(rockitem.durability)

would work, but it doesn’t, because when I return to the main scene the rocks still take 4 hits to destroy, any ideas?

I think you can solve this problem by creating a rock.tres resource with durability property and using it in all rock nodes.

then in shop just load the resource and decrease durability.

As the resource is shared among all instances (according to Godot documentation on resources) unless made unique/local to scene/duplicate, I think it should help.

@sak A tip for you in future, instead ask for guide in a concept you don’t understand, say what you have, what you want to happen and what is happening with your current code. In the post i answered you i didn’t catch what you really wanted at first, in this one the same thing and the other post you did is the same thing. Also post your code, people need to know what you have to guide you.

For answer you question, you need to get the reference of every node you want to affect, from the code you put in the other post:

# Rock script

extends Button
class_name rock

# No needed
#@onready var rockscript = get_tree().get_first_node_in_group("rock")

# Create a signal to be emitted when the rock breaks
signal broken

var tween_hover: Tween
var durability: int = 4

# No needed
#var count: int = 0

func _on_mouse_entered() -> void:
	if tween_hover and tween_hover.is_running():
		tween_hover.kill()
	tween_hover = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_ELASTIC)
	tween_hover.tween_property(self, "scale", Vector2(1.05, 1.05), 0.5)


func _on_mouse_exited() -> void:
	if tween_hover and tween_hover.is_running():
		tween_hover.kill()
	tween_hover = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_ELASTIC)
	tween_hover.tween_property(self, "scale", Vector2.ONE, 0.5)

func _on_button_down() -> void:
	durability -= 1
	if durability == 0:
		self.visible = false
		# When the rock breaks, not only hide them but
		# also delete the rock and emit the signal 
		# that the rock broke
		broken.emit()
		queue_free()
		
	if tween_hover and tween_hover.is_running():
		tween_hover.kill()
	tween_hover = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_ELASTIC)
	tween_hover.tween_property(self, "scale", Vector2.ONE, 0.5)


func _on_button_up() -> void:
	if tween_hover and tween_hover.is_running():
		tween_hover.kill()
	tween_hover = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_ELASTIC)
	tween_hover.tween_property(self, "scale", Vector2(1.05, 1.05), 0.5)

# Spawner script

extends Node2D
class_name spawner

# This don't need to use the @onready, use @onready
# when you're getting a node reference 
var ROCK = preload("res://scenes/rock.tscn")

# No needed
#var spawn_index: int

# Don't need to set right now, _spawn will do that
var spawn_count := 0


var spawn_list = [
	Vector2(0, 0),
	Vector2(32, 0),
	Vector2(64, 0),
	Vector2(96, 0),
	Vector2(0, 32),
	Vector2(32, 32),
	Vector2(64, 32),
	Vector2(96, 32),
	Vector2(0, 64),
	Vector2(32, 64),
	Vector2(64, 64),
	Vector2(96, 64),
	Vector2(0, 96),
	Vector2(32, 96),
	Vector2(64, 96),
	Vector2(96, 96)
]


# Use the _ready callback for do the first spawn,
# ready will be called when the scene is added
# in the scene tree for the first time
func _ready() -> void:
	_spawn()


# You don't need to do this checks in the _process callback
#func _process(delta: float) -> void:
#	_spawn()
#	print()


# Also this can be removed
#func _spawn_check() -> void:
#	if (get_child_count() < 4 or ROCK.rockscript.count == spawn_count) && spawn_count > 0:
#		_spawn()


func _spawn() -> void:
	# You only use this function when needs to do a 
	# new spawn, so move this to here
	_rand_spawn_count()
	
	# Create a new copy of the spawns positions array, so
	# You can modify this array without lose the positions
	# for do another respawn
	var temp_spawn_positions = spawn_list.duplicate()
	
	# Use a for loop to repeat the spawning process
	# the necessary amount of times to spawn all the rocks
	for i in range(0, spawn_count):
		var instance: Button = ROCK.instantiate()
		get_tree().get_first_node_in_group("main").add_child(instance)
		
		
		# Get a random rock position
		var rock_position = temp_spawn_positions.pick_random()
		instance.position = rock_position
		
		# Remove the used position to not be used again
		# in this spawn wave
		temp_spawn_positions.erase(rock_position)
		
		# Rember the broken signal in the rock? We'll
		# use now, so when the rock breaks, the rock 
		# code will call the _on_rock_break function
		# so you can do all the checks there
		instance.broken.connect(_on_rock_break)
	
	
	# Old code, i'll let some observations here for you
	#var spawn_rate = randi() % 20 + 1
	#if spawn_rate == 1:
	
		# This will fail because you never setted this
		# so will be zero
		#if spawn_index != 0:
		
			#var instance: Button = ROCK.instantiate()
			#get_tree().get_first_node_in_group("main").add_child(instance)
			#var pos = randi() % spawn_index
			#instance.position = spawn_list[pos]
			
			# You removed the position here and never added again
			# your list will be smaller and smaller ever respawn
			# until you won't have enough positions
			#spawn_list.remove_at(pos)
			
			#spawn_index -= 1
			#spawn_count -= 1


# This function can be simplified to direct 
# set the script variable
func _rand_spawn_count() -> void:
	# Not a good idea have two variables with same
	# name like that in the same script, can
	# lead to silly mistakes
	#var spawn_count = randi() % 10 + 1
	#return spawn_count
	
	spawn_count = randi() % 10 + 1


func _on_rock_break() -> void:
	spawn_count -= 1
	
	# No more rocks? Get a new respawn
	if spawn_count == 0:
		_spawn()

You add your rocks as child of a node inside the group “main”, so you can get all of your rocks by calling get_children() in the node from “main” group

var main_node = get_tree().get_first_node_in_group("main")

# This will return an array with all the nodes that are child of main_node
var rocks_array = main_node.get_children()

# Now you can iterate one by one using a for loop
for rock in rocks_array:
	# That will be called for every rock inside the array.
	# You now have a referece for the rocks in the rock variable, do your stuff
	rock.durability -= 1

Hey thanks for the reply, I figured it out eventually, turns out using global scripts and singletons is a lot more useful than I thought

Thanks man, I appreciate all the help, been very useful to learn not only godot, but also the godot forums. I’ll take your advice from here out