Signals are melting my brain. A little nudge please?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Robster
:warning: Old Version Published before Godot 3 was released.

Hi all,

I’m testing my first ever custom signal.
In this test, I have it working so when I press 1, 2 or 3 on the keyboard, the speed of my bat changes. This works so that’s great.

Now I want it so when the speed changes, a signal is emitted. (I believe I have this part correct). When that signal is emitted, a function is called to change the text of a label.

Except it doesn’t. Can anyone offer some advice as to what I’ve done wrong? This is my first custom signal. Thank you.

#script: gameLevel.gd
extends Node

onready var speedLabelL = get_node("/root/gameLevel/HUD/SpeedL")

signal batSpeedChanged

func _ready():

	if OS.is_debug_build():
		print("debug mode")

	if speedLabelL:
        print("We have a label")
		speedLabelL.connect("batSpeedChanged", self, "updateScoresEtc")

	set_process_input(true)

func _input(event):
	if OS.is_debug_build():
		if Input.is_key_pressed(KEY_1):
			changeBatSpeed(0,1)
		elif Input.is_key_pressed(KEY_2):
			changeBatSpeed(0,2)
		elif Input.is_key_pressed(KEY_3):
			changeBatSpeed(0,3)

func changeBatSpeed(bat, speed):
	if bat == 0: #left bat
		game.batSpeedL = speed
	elif bat == 1: #right bat
		game.batSpeedR = speed

	if OS.is_debug_build():
		print("Bat speed = ", str(speed), " for bat # ", str(bat))

	emit_signal("batSpeedChanged", bat, speed)


func updateScoresEtc(bat, speed):
	if OS.is_debug_build():
		print("signal should happen with bat", str(bat), " and speed of ", str(speed))
        print("This is where we'll change the label text")

Just a note, all the print statements work except for the last one in the updateScoresEtc function.

You should avoid getting nodes outside of the current scene. In your code you are calling get_node("/root..., which only works if there is the correct node at the hardcoded path.
But the main point of using signals is to be able to add a function call in an object before you even know which object is the one containing the function to be called. So your script can change the bat speed and call some function that should be called when the speed changes without knowing which function that is.
If you have a reference to the speed label in your script you can simply call the correct method on the label.

Tl,dr: put both the label and the speed changing object into one scene and have that new scene that owns both objects connect the signal with the label’s function.

Warlaan | 2017-02-08 16:59

Thank you. I came to this conclusion last night. Once I resolved the current code I sat and thought about the ways I could use signals. At first I was excited about the opportunities in terms of features (bad guys being spawned. Player collides with an object. Player dies. Bullets fired. Etc). Then I got excited about implementation. The idea that I can write a listener of sorts, attached to an object. Then all I have to do is send the signals, from ANYWHERE and the listener will receive the signal. That’s incredible in terms of simplifying code. This game I’m working on (pong) is my third game and if I’d have known about signals properly in the last two games I’d have done them very differently. But, such is the learning process.

Robster | 2017-02-08 21:33

:bust_in_silhouette: Reply From: vinod

You should define the signal on the node which emits it. Now it is defined in the listener node.

A simple example would be like this:

Player script

extends Sprite

signal hurt

func _ready():
    pass

func hit_by_sword():
    #the player got attacked
    emit_signal("hurt")

Observer node, ie., some HUD

extends Label
func _ready():
    var player = get_node("path/to/player/node")
    player.connect("hurt",self,"_on_player_hurt")

func _on_player_hurt():
    print('Player got hurt")

Thank you! That works perfectly. I had no idea that you couldn’t emit and receive a signal in the one script file. I have it working now.

Robster | 2017-02-08 10:24

:bust_in_silhouette: Reply From: avencherus

I had no idea that you couldn’t emit and receive a signal in the one
script file.

You can. Though the intention of signals are to allow you to decouple classes. So they don’t have direct dependencies. They just listen for a signal, they don’t care who does it or how it gets done. So if it’s all code inside the same object, it’s hard to imagine a case why you wouldn’t just directly perform the function or actions you want done.

I tested your code, and as far as I can tell your signal code works just fine. If there are issues with the label your bug may be elsewhere.

There must have been another issue yes. Since moving it though I’ve corrected whatever it was which worked out but at least I understand them better now. You’re right, why use a signal in the same script when you can just reference a function. Silly. But, it’s starting to make sense so thank you.

Robster | 2017-02-08 23:16

You’re welcome.

avencherus | 2017-02-09 00:18