Notifying a node of a collision?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By sebandseb

I have a node which uses a RayCast2D to identify a colliding node, which could be of several different types, some of which don’t care about the collision. All the nodes are instanced through code.

I would basically like to ‘inform’ the 2nd node of the collision, while passing some data to it from the 1st node, and allow it to react to this as it will, including ignoring it completely.

It feels like there must be a clean way to do this with signals or something, but I’ve been stuck on it for a while now, not sure if there’s something obvious I’m missing.

Or, should I just be calling a method on the 2nd node from the 1st directly, and making sure that any node the raycast will collide with has it, leaving it empty if needed?

:bust_in_silhouette: Reply From: jgodfrey

Either way will work (signal or a direct call to a method on the other node). If you want to use the direct call method, you don’t need to have “empty” functions on the objects you’re not interested in. Instead, Godot has a has_method() function that will tell you if a given object has a given method.

So, using that, you can simply see if the collided object has the method you’re interested in and, if so, call it directly.

Thanks!

I’m sure using has_method() will work for what I need here, but would you be able to go into more detail about how you would go about using signals? I’m pretty new to Godot and gamedev in general and still getting my head around a lot of the patterns.

What I tried before was:

  • declare signal ‘hit_something’ in 1st node
  • after finding a collision and getting the target node, doing:
    target.connect('hit_something', target, 'react_to_hit', [data]) emit_signal("beam_incident")
  • then in the target node have a ‘react_to_hit’ method

However it gives the error ‘attempt to connect non-existent signal ‘hit_something’’, which I guess makes sense since connect() is called on the target but the signal is declared in the 1st node.

Apart from that it just doesn’t feel I’m using signals the way they’re supposed to be used here, making the connection to a specific target and then immediately emitting the signal meant specifically for that target…

sebandseb | 2020-03-21 22:58

Generally, signals provide a mechanism for communicating between various objects without requiring the objects to be aware of each other. Receiving objects can subscribe to signals as needed and broadcasting objects can emit signals as needed, and none of them need to know about the others - or even care whether they exist.

While you could use signals for your specific case, it doesn’t really feel like the right tool for the job. You could broadcast a general collision signal, but since you only want one object to react to it - and you already have a direct reference to that object (via the collision information), I think the simplest and most efficient mechanism would be to do a direct function call on the collided object (coupled with a has_method() filter if necessary).

So, that’s what I’d probably do in this case.

However, here’s some basic signal management advice - maybe you’ll find a use for it…

You can make signals easier to access / use by implementing a simple “event bus” system. For example, you can define all of your signals in a single script defined as a singleton. So, for example:

Events.gd

extends Node

#warning-ignore-all:unused_signal

# move platform down the screen by the specified delta
signal move_platform_down(yDelta)

# a new max height has been reached
signal new_max_height(height)

# set the jetpack counter to the specified value
signal set_jetpack_count(jetPackCount)

signal set_coin_count(coinCount)
signal collect_coin(coin) 

Be sure to load Events.gd as a singleton via Project | Project Settings | Autoload

With that in place, any script can subscribe to events as necessary (typically, in their local _ready function). For example:

Platform.gd

func _ready():
	Events.connect("move_platform_down", self, "_on_move_platform_down")

func _on_move_platform_down(yDelta):
   # do stuff with yDelta

Likewise, any script can emit one of the registered signals as needed. For example:

Player.gd

Events.emit_signal("move_platform_down", maxScreenYPos - pos.y)

jgodfrey | 2020-03-21 23:58

Great, thanks so much, makes perfect sense - it definitely looks like I was tripping over myself trying to make them do something they aren’t meant for!

Cheers for the extra info also, all helpful stuff.

sebandseb | 2020-03-22 07:04