+1 vote

I am trying to create a scene where I spawn instances of a scene that send a signal once they connect. Think of something like Crossyroad or Frogger.

I have the following setup:
A player scene that has a KinematicBody2D with a sprite and a collisionshape (and some code to move it)

An enemy scene that where an Area2D is the root node and it has a sprite and a collisionshape2D as well and in the code I have:

extends Area2D

func _ process(delta):
    position.x -= 5

(I move the parent node to the right edge of the viewport).

Finally, there is a main scene where I try to connect them, it just has a plain Node, an instance of the Player and a timer that emits the timeout() signal every 2 seconds, the code on the script looks like this:

extends Node
const new_enemy = preload("res://Enemy.tscn")
func _on_Timer_timeout():
    var enemy = new_enemy.instance()
    get_parent().add_child(enemy)
    enemy.connect("body_entered",get_parent(),"test_function")

func test_function():
    print("collision")

When I run the scene and the player touches the enemy I get the error message:

Error calling method from signal 'body_entered':'Viewpoert::test_function': Method not found

I am mostly confused about the .connect("signal",node,"function"). Would really appreciate if someone could explain where I went wrong.

asked Jan 25 in Engine by 1izNoob (205 points)

1 Answer

+1 vote

When you write

enemy.connect("body_entered",get_parent(),"test_function")

You're saying that you want to connect the "bodyentered" signal from enemy to the "testfunction" method on the current node's parent. Since the method is in the current node, you should put self there.

The syntax of the connect method is

<emitting_node>.connect("signal_name", <target_node>, "target_method_name")
answered Jan 25 by kidscancode (17,016 points)

Hey, thank you so much! But I seem to be getting another error message

Error calling method from signal 'body_entered':'Node(Main.gd)::test_function': Method expected 0 arguments, but called with 1

Can you help with that as well? Thank you so much in advance!
(Your tutorials are amazing by the way :) )

The body_entered signal comes with an argument. It passes a reference to the body that entered. Your receiving function must accept an argument.

I am so sorry for being so incompetent and taking so much of your time.

I changed the test function to accept an argument, like so:

func test_function(body_entered):
    print("test")
    print(body_entered)

(all in the script of the Main node)

But I keep on getting the same error message. Do I need to do something else to the function? I am really confused about it. And thank you again so much!

Can you show your code again? The main script and the one that's spawning the enemies.

Yes, sure! Thank you

So the main scene has those nodes:
Node(called main)
- Player (instanced)
- Timer

And the script on the main code looks like this:

extends Node

const new_enemy = preload("res://Enemy.tscn")

func _on_Timer_timeout():
    var enemy = new_enemy.instance()
    get_parent().add_child(enemy)
    enemy.connect("body_entered",self,"test_function")

func test_function(body_enter):
    print("test")
    print(body_enter)

And the Enemy scene has these nodes:
Area2D (called Enemy)
- Sprite
- Collisionshape2D

And the code on the Area2D is:

extends Area2D

func _process(delta):
    position.x -= 5

    if get_overlapping_bodies():
        emit_signal("body_entered")

Oh, while testing the code again I realised that the testfunction does print the two statements but I didn't see it because I had the debugger open with the error messages, which still appear. So I get both the error message from before (Method expected 1 arguments but called with 0) and the expected code from the testfunction. Any idea?

Why are you manually emitting the body_entered signal? It's automatically emitted when a body enters the area.

Are you sure you haven't also connected the signal in the enemy via the Inspector? There's some other piece of information you haven't given me.

Manually calling the body_entered signal caused the problem, thank you so much!

And I didn't touch the node tab at all, the entire thing was mostly to learn how to manually use signals in code.

Just so I understand the signals: So all the signals are always executed and emitted but that other nodes can only receive them when they are explicitly connected? Hope that question makes sense. In the context of Area2D, it checks all the signals starting from areaentered all the way down to scriptchange and executes them?

I'm not sure I entirely understand your question.

Signals are not "executed". They are emitted when the associated event occurs. In the case of built-in ones, that's predefined. For custom signals, it's whenever you call emit_signal(). There is no "checking all the signals" - when you change a script, script_changed is emitted. Yes, signals are always emitted regardless of what's connected - that's the point of them. The emitting node doesn't need to know who's listening and doing what when they're emitted.

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 How to use this Q&A? before posting your first questions.