0 votes

I have a player class with a simple signal:

extends KinematicBody2D
...
signal player_moved(position, facing)

I also have a WarpAtBoundaries class which tries to connect to this signal:

extends Node

func _ready():
    print("Auto-warp reporting")
    # Called when the node is added to the scene for the first time.
    # Initialization here
    self.connect("player_moved", self, "_on_player_moved")

func _on_player_moved(position, facing):
    print("Player moved to " + str(position) + " and is facing " + facing)

In the debugger, I see an error that Attempt to connect nonexistent signal "player_moved" to .... I see the auto-warp reporting message, but not the print of the player's coordinates.

My hierarchy for these two nodes:
- Start.tscn is a node with a Player instance
- Start.gd calls load(...).instance() to create an instance of a map
- That map (eg. Meadow1.gd) calls load("WarpAtBoundaries.gd").new()

I don't understand why the signal isn't being defined / being connected correctly. I can add print statements and see the event is firing, but I can't connect/receive it.

I tried calling load("Player.gd") in the warp class, and adding a singleton "SignalManager" class like this post, thinking that the warp class doesn't know about the signal; but the error persists.

in Engine by (78 points)
edited by

2 Answers

+6 votes
Best answer

While Xrayez's answer was critical to understanding the syntax of emit_signal and connect, ultimately, I went with a completely different approach:

1) I created a SignalManager class which contains all my signals, eg. player_moved
2) I configured my Godot project to AutoLoad this script as a singleton
3) Whenever the player moves, I call SignalManager.emit_signal("player_moved", ...)
4) In other classes that care, I call SignalManager.connect("player_moved", self, "_on_player_moved")
5) I add a method _on_player_moved to that class
6) Profit

by (78 points)

Just would like to show my gratitude for your response, helped me a lot!

Glad to hear it helped someone else! Ultimately, it's not how Godot's designed to work, and it has some downsides like global state, but IMO it's much simpler to use overall.

Your solution was the best I ever seen. Helped me a lot!
Thanks.

Love this method to painlessly manage signals! Signed up just to upvote!

+2 votes

You need to fetch the Player instance first, and connect its signal into WarpAtBoundaries instance:

onready var player = load("Player.gd").new()

func _ready():
    player.connect("player_moved", self, "_on_player_moved")

self is WarpAtBoundaries instance, and because it doesn't define player_moved signal, you get the described error.

As you stated, you have your Startscene which acts as a main scene I suppose. If you already instance a player in that scene:

onready var player = $player # or get_node("player") in Godot 2

Or in a singleton like Characters:

const Player = preload("player.gd")
var player = Player.new()

And then in your Start scene:

Characters.player.connect("player_moved", self, "_on_player_moved")

Depending on whether the character can be removed from the scene, you need to connect the signal dynamically if you want to add new player. For that, the SceneTree (fetched with get_tree()) has node_added(node) signal which can be connected to your Start scene like this:

func _ready():
    get_tree().connect("node_added", self, "_on_node_added")

 func _on_node_added(node):
    if node is Characters.Player:
        var player = node
        player.connect("player_moved", WarpAtBoundaries, "_on_player_moved")

The reference to the player will be always kept in the singleton unless you explicitly queue_free() it or replace it with the new player.

This might be over-complicated but it really depends how big your game is going to be.

by (1,422 points)
edited by

Okay, this makes sense.

I don't want to create a new Player instance. because I have a pre-existing one. This brings me full-circle to a previous problem: how can I elegantly handle keeping a single(ton) reference of Player? Godot doesn't support static variables from what I understand.

See my updated answer :)

That's cool, thanks for this. I think the linked code is more like what I want (global signal handling); I'm still confused why SignalManager.connect("player_death", "self", "on_player_death") gave the same error. I'll try this tonight and let you know how it works out. Thanks again!

Thanks for your help. I ultimately went with a simpler approach of centralizing my signal management into a single class.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.