Error connecting a signal(s) from a group to a node via code

Godot Version

4.2/4.3 (tested in both)

Question

I’m trying to connect a signal from all the nodes in a group.

to explain my situation, I’m creating a basic temperature system for my game. Currently, I’ve created a scene that can be instantiated onto a 3d node in the game world to turn that object into a heat source. all of the heat sources exist as a direct child of the root scene, and the player (currently the only heat receiver) does as well. I have a for loop where “i” = some member of the heat receivers group. I’m trying to use the connect method on the heat_source_modifier that is instantiated in “i”, whenever I try to pass in the name of the function that I want to call and the callable (which I understand to be the function in the script that is executed upon calling the signal). It seems as if the function that I would like to be called is simply not being called; however, the code runs fine and the connection seems to be valid, since I don’t get debug messages. I set a breakpoint at the Player’s modify_heat function and it never triggers. Heres the relevant code:

player.gd

func _ready() -> void:
	for i in get_tree().get_nodes_in_group("Heat Sources"):
		i.find_child("heat_source_modifier").connect("modify_heat", modify_heat)

func modify_heat(heat_modifier: Variant, target: Variant) -> void:
	if target == get_name():
		coldness -= heat_modifier

heat_source_modifier.gd

extends Node3D
@export var heat_radius = 1.0
@export var decay_per_meter = 1.1
@export var heat_strength = 1.0
@export var heat_origin = Vector3(0,0,0)
@export var heat_size = Vector3(.1,.1,.1)
signal modify_heat(heat_modifier, target)
var heat_recievers := []
var heat_modifier = 0.0

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	$Area3D/outer_layer.shape.set_radius(heat_radius)
	$center.position = heat_origin
	$center.scale = heat_size
	get_parent().add_to_group("Heat Sources")


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	var timer = 0.0
	if timer>1.0:
		timer=0.0
		if heat_recievers.size()>0:
			for i in heat_recievers:
				heat_modifier = heat_strength / (i.distance_to($center) * decay_per_meter) 
				modify_heat.emit(heat_modifier, i)
	timer += delta

func _on_area_3d_area_entered(area: Area3D) -> void:
	if area.get_parent().is_in_group("Heat Reciever"):
		heat_recievers.append(area.get_parent_node_3d().get_name())


func _on_area_3d_area_exited(area: Area3D) -> void:
	if area.get_parent().is_in_group("Heat Reciever"):
		heat_recievers.remove_at(heat_recievers.find(area.get_parent_node_3d().get_name()))

Is my implementation approach fundamentally flawed, do I have a random logic error I haven’t noticed, or is there some syntax issue that isn’t causing a crash?

Any help would be greatly appreciated! :sob:

It looks like you’re adding the nodes to the “Heat Sources” group via code, are you sure that runs before the player’s ready function that connects all the modify_heat signals?

2 Likes

that seems to have been an issue, because whenever I added it to the group via the editor rather than code I get an actual compilation error. it’s at the line: i.find_child("heat_source_modifier").connect("modify_heat", modify_heat) and the error is “Cannot call method 'connect' on null value.” I tried adding a != null if-statement before the line, but that didn’t help at all.

once I’ve got these bugs worked out I’d go back to loading it via code, and just ensure via the game startup script that the heat receivers load first. It appears I don’t fully understand how groups work though, because I thought that during runtime you could add or remove a group via code and it would just be appended or removed from the group, like an array.

Thanks for the help so far btw!

1 Like

You can add and remove groups via script, it’s just that I don’t know the execution order of ready in your project so it might be added after the player connects the signals. I suspect that might be the case since the connect code only runs when you add groups in editor

the error means that it’s not finding the node “heat_source_modifier”, did you spell the name correctly with capitalization and everything?

oh good point, I’m going to shift the connect to _process (having it skip over already connected signals) and report back

i don’t think you should do that, you don’t need to constantly check for new heat source nodes. that won’t resolve the null error either

if “heat_source_modifier” is the exact name of a child node of a heat source then maybe it isn’t ready and you do need to wait a frame longer, try await get_tree().process_frame before going into _process itself.

But I doubt the Heat Source doesn’t have it’s children ready when it is ready. Double check the names

the way my games structured i think I may need to, considering I plan to be instancing the heat_sources via code. I’m probably wrong tho.

you can just connect the signal in the same code block as instancing the heat source.

anyway to ensure that the player’s ready is called after the heat source ready just move it higher in the scene tree, the ready functions are always called in a specific order

Sounds like a Autoload would fit better, having to track a bunch of nodes leaving and entering the scene is a lot of work when one could instead flip the logic, have the heat sources report changes to a global authority.

I just rearranged it; it still reports the null value.

okay does the node in the heat source group have a child called “heat_source_modifier” exactly spelled and capitalized that way, with underscores and all

I agree, this happens to be a collaborative project and a system like this has been discussed, I’m not the team member that’s supposed to be doing it, but I may just go ahead and do it if it makes this easier.

A global authority, I mean.

I just triple-checked, you two were right.

1 Like

Ok, after fixing that embarrassing simple typo, it compiles again. I’m back to square one though, I added a breakpoint to my function on player.gd, and it never breaks.

where is the breakpoint? inside the modify_heat function?

yes. at the coldness -= heat_modifier point to be exact

just added one to the actual func line as well, doesn’t break either.

timer variable will never be greater than 1 because it’s local to the process function and will be destroyed every frame, you need to declare it outside the process function

:man_facepalming: of course!

1 Like

also adding a breakpoint to the func line doesn’t do anything

1 Like