Help with signals/scripts communicating with each other

Godot Version

4.3

Question

I have been following Brackey’s tutorial here, and have run into a snag when handling the player’s death.

What I’ve done (in following the tutorial) is to create a Scene called “killzone” (35:30 in the video if you care to watch it) which can then have some sort of CollisionShape2d attached to it to make it multipurpose. The intent here, as I understand it, is to have a “general purpose” Scene that can be used in a variety of ways to achieve the same end result: handle the player’s death.

In addition, the player character has it’s own Scene and associated script to handle movement, animation, etc.

My goal here is to expand the death functionality of the game so that the player’s speed drops to 0 and the game plays a death animation while the game restarts.

As I understand it, there are two ways to do this:

Create a reference to the player.gd script to directly modify the speed and animation from within the killzone.gd script. The tutorial made it sound like I could ctrl+click drag scenes into scripts in order to make references to them (and their subordinates), but that doesn’t seem to work when I try it with the killzone.gd script.

or

Use a signal in the killzone.gd that player.gd watches for and uses as a cue to set the player speed to 0 and change the animation. But when I put

signal isdead(status)

in the killzone.gd script, I get the following error on that line:

Unexpected "Identifier" in class body

As is likely very obvious at this point, I am quite new to GDscript and how different scripts interact with each other. There is clearly something fundamental I am missing here and I’m hoping someone can point me in the direction of some documentation or guide that will explain what I’m doing wrong and maybe even suggest a better way to do what I’m trying to do. Thank you in advance for your help!

killzone.gd:

extends Area2D

@onready var timer: Timer = $Timer
signal isdead(status)
emit_signal("isdead",0)

func _on_body_entered(body: Node2D) -> void:
	print("you died")
	emit_signal("isdead",1)
	Engine.time_scale = .75
	timer.start()

func _on_timer_timeout() -> void:
	Engine.time_scale = 1
	emit_signal("isdead",0)
	get_tree().reload_current_scene()

player.gd:


extends CharacterBody2D

var SPEED = 130.0
const JUMP_VELOCITY = -300.0
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var direction := Input.get_axis("move_left","move_right")
	
	if direction > 0:
		animated_sprite.flip_h = false
	elif direction < 0:
		animated_sprite.flip_h = true
	
	if is_on_floor():
		if direction == 0:
			animated_sprite.play("idle")
		else:
			animated_sprite.play("run")
	else:
		animated_sprite.play("jump")
	
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()

The error is actually caused by this line. Put it to a function or remove it completely. In GDScript, only declarations (constants, variables, signals, …) are allowed outside the function bodies.

1 Like

I would also suggest using isdead.emit(0) syntax instead. It’s type safe as opposed to emit_signal()

Thank you both for your replies! I appreciate you both taking the time to help me!

Follow up question: how do I implement the signal into my player.gd?

Right now I have

func _on_killzone_isdead(status: Variant) -> void:
	if status > 0:
		print("pass")
		pass
	else:
		print("u ded")
		SPEED = 0
		animated_sprite.play("die")

appended to the end of player.gd, and in the scope of the _physics_process function I put the line _onkillzone_isdead(SPEED)

I also changed the two lines in killzone.gd to isdead.emit(1) and isdead.emit(0)

When I run the game, the output log spams ‘pass’, which makes sense, since the function is being called and immediately passed. However, when the player character dies, the _on_killzone_isdead function never seems to make it to the else loop. I never see ‘u ded,’ which I’m interpreting to mean the status variable isn’t being looked at for some reason. Can anyone explain what I’m doing wrong?

Edit: I just realized the if statement is incorrect given the values I’m passing.

You never connected your signal in the killzone.gd script to the function in the player.gd script.
You need a reference either from your Player to your Killzone or the other way around.
Add this to your killzone.gd script at the top:

class_name Killzone

Then add this to your player.gd script somewhere near the variables at the top:

@export var killzone: Killzone

Then add this to your player.gd script:

func ready() -> void:
	killzone.isdead.connect(_on_killzone_isdead)

Then, just drag&drop your Killzone node in the Inspector to the Player’s variable.
image
I would also change the type of the parameter function from Variant to float, as this is what you’re passing.

_on_killzone_isdead(status: float)

Okay that appears to be working, sort of.

It looks like what’s happening is the _on_killzone_isdead function is firing right away, which is intentional, but the value being passed to it is not 0, so right at the very very beginning of the game, the else part of the function is firing, which locks the player out. Which tells me I need to set that to 0 right away. Putting

func ready() -> void:
	isdead.emit(0)

before the _on_body_entered function in killzone.gd doesn’t seem to do anything. The hope was to set the variable right away to avoid this.

In general, is this even a good way to do this? I feel like this is good practice for using signals in general, but I’m wondering if I’m just needlessly complicating things.

Actually, I think I just realized what I’m doing wrong (stream of consciousness typing ftw):

I use the following line to invoke the signal check:

_on_killzone_isdead(SPEED), and SPEED is not 0, so of course the function isn’t working properly.

What I need to do is look at status in isdead but I’m not sure how to get Godot to look at that and not something else. The whole point here is the player.gd is looking somewhere else for a value rather than in itself.

As an aside, I really appreciate you taking the time to answer my dumb questions. I swear I looked at the documentation to try and answer my questions, but if you have a YouTube video handy that will help, I’d be happy to watch it.