Connecting Signals Manually

So out of curiosity and also to drill the approach for memory, I was trying to connect the “took_damage” signal to the Game scene from the script. This is my code thus far:

|extends Node2D
|
|var lives = 3
|
|@onready var player = $Player
|
|func _ready():
| player.took_damage.connect(_on_player_took_damage)
|
|func _on_player_took_damage():
| lives -=1

This seems to work. But Kaan seems to be connecting this signal from the Player scene directly to the Game scene (rather than using the player instance). I tried to do this manually with the code:

|@onready var game = $"."
|
|func _ready():
| game.took_damage.connect(_on_player_took_damage)
|
|func _on_player_took_damage():
| etc. etc.

Now besides just making less sense to me, this also just doesn’t work! I receive the error: “Invalid get index ‘took_damage’ (on base: Node2D (game.gd)’).” which actually makes total sense - why would the Game scene be able to respond to the took_damage signal, and why would I want it to? I’m unsure why the Game node is showing up as “.” when I drag it into the script, but I assume it has something to do with it being the root of the scene?

I guess my confusion here probably stems from not quite understanding how connecting signals through the node menu actually works under the hood. My question is: when Kaan connects the “took_damage” signal, is he actually connecting directly to the game scene? If so, is there anything wrong with connecting to the player instance? Is there a correct way to connect to the Game scene with code?

Just trying to understand a bit more what’s actually happening behind the scenes here, thanks!

I checked the video; what Kaan is actually doing is connecting [the signal on the Player instance] to the Game scene’s root. It’s actually instance-to-instance, despite all of the following factors:

  • Player shows as a scene in the scene tree (movie clapper icon) - this is still an instance.
  • The Game node is the scene root. When the scene is instanced, this node is part of that instance (if you were to instantiate the Game scene 5 times at once, they wouldn’t communicate with each other in this way by default because they are separate scene trees).
  • Scripts are attached to all instances of a scene (but signal connections defined in the Inspector are not; they are instance-to-instance)
  • Kaan says at approx. 6:30 that “this will connect the took_damage() signal to the game script” (technically true, but an unintentionally misleading statement - it’s connecting to the game instance’s copy of the game script)

As far as I’m aware, you can’t connect signals directly between disparate scenes because they are not instantiated together in any scene tree - nothing exists yet to connect. What you can do is dynamically connect signals to any relevant instances that appear, and Kaan actually does this in the Game script when he connects enemy_instance.destroyed() to on_enemy_destroyed() (names might be slightly different; I modified this code a lot). That example really showcases how useful dynamic signal connection can be ^v^

This happens when you attempt to add [whatever node the script is attached to] as a reference - a self-reference basically. It isn’t needed. If the code worked, your game.took_damage.connect(_on_player_took_damage) could be simplified to remove game. as a result.

Don’t worry, signals can take a while to wrap your head around =)

1 Like

Ohhh okay that makes so much more sense! Yeah I was stuck on the idea that the signal was connecting across disparate scenes - looking back on my code I guess that game.took_damage.connect() was looking for a signal within the game scene to connect back to itself, whoops!

Thanks so much for breaking this down, I’m feeling one step closer to fluency ^^

2 Likes

:+1:

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

Privacy & Terms