How do I pass along additional information with a pre-configured signal, or access the emitter's node?

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

Hi everyone,

I am pretty new to Godot, but have previously dabbled a bit with C++.

I feel like I am missing something obvious, or maybe I am using signals for something they are not intended. I’d greatly appreciate some feedback.

In essence, I want to create a small program in which “hunters” walk around randomly until they “see” some (so far) stationary “prey”. Both prey and hunters are created via code and therefore don’t have static names.

I am using Area2Ds and CollisionShapes as the elemental basis for the different objects noting they are in each other’s spheres of influence. In particular, the hunters have two areas associated with them, one larger (their “seeing area”) and one smaller "their “eating area”).

Scene 1:

  • Node2D (Hunter)
    • Area2D (Seeing Range)
      • CollisionShape
    • Area2D (Eating Range)
      • CollisionShape
        -Sprite

Scene 2

  • Area2D (Prey)
    • Collision Shape
    • Sprite

Then I am using signals, in particular “on_area_entered”, to trigger the intended behaviour. However, I’d need to pass on some additional information stored within the different prey objects as part of the signal, for the hunter to act accordingly. (e.g. if there are different types of prey, but the hunter is only meant to react to some of them, but not others).

I’ve followed various avenues, all unsuccessful, so far:

  • Connecting the signal “on_area_entered” from the hunter to itself. The area argument passed by the signal then contains the area of the prey. However, I cannot access additional information from that area (e.g. area.typeofprey), because I can only access the standard variables of area2a (e.g. area.position). I can also not call any functions in the prey, for the same reason (eg. area.foo() )
$SeeingRange.connect("area_entered",self,"_on_hunter_area_entered")

func _on_hunter_area_entered(area):
how to get custom variable area.type?
  • As a workaround to the above, I wanted to use the “name” attribute of the area being passed for additional information, but that is autogenerated, e.g. “@Prey@02” or so. Also, even if I could get it working in this instance, it might fail in others, when I’d need to pass on more complex information.
connect("area_entered",self,"_on_hunter_area_entered")

func _on_hunter_area_entered(area):
if area.name == "rabbit":
foo()
  • Connecting the signal “on_area_entered” from the prey to the hunter. Then I should be able to pass on information as additional arguments by amending the connect function, though I have struggled to get that working as the arguments being passed are often ‘null’. Even if I did get it working, I’d lose the information which of the two areas in the hunter node are overlapping with the prey area.
connect("area_entered",hunter,"_on_Target_area_entered",[type])

func _on_prey_area_entered
how to get the information on whether prey now overlaps with the Seeing_Range or 
the Eating_Range area of the hunter scene?
why is type null? At least when I tried to pass they prey's position? (haven't tried many 
more attributes)

So, my question is: Is there any way to get the proper node of the signal emitter, so that I could access its variables, or call functions?

And in case of “No, that’s not how signals should be used, fool!”, then is there a way I can use Area2Ds and collision shapes for my intended purpose but without using signals, but direct function calls between the hunters and the prey?

Many thanks!

:bust_in_silhouette: Reply From: Zylann

I’m not sure if I correctly understand what blocks you here, but it sounds like you could do the following:

Each hunter has a SeeingArea and an EatingArea. So far you used area_entered, so I assume your prey is an area. That’s what the doc says: Emitted when another area enters..

Coming from here, you can connect the two areas of your hunter to two functions, on_seeing_area_entered and on_eating_area_entered. From this point, you know which one is getting entered, depending on which function gets called. Node names don’t matter.
If that technique is not possible for whatever reason, you can add an extra argument at connection time, like this:

area.connect("area_entered", listener, "on_area_entered", [area])

Then add an extra parameter to your on_area_entered function, which will receive the extra value.

area_entered then gives you the prey area which entered your hunter area. So you know the prey as well.
If that area is actually not the prey but some area inside it, make sure to add some getter function on that area so that anything that detects it will be able to obtain the prey it belongs to (which I believe is just the parent node most of the time). You could also just make the root node of the prey the area itself, or make a convention in your project that those areas are always child of the prey.

Does this make sense?

There are alternative ways to do such detections like using shape queries every frame in _physics_process, but I tried to explain how you can do it with a node setup like yours.

Thank you so much for answering, despite my cryptic question. I was pretty sure it was difficult to understand, but I struggled to formulate it any better with the limited understanding I have so far.

If that area is actually not the prey but some area inside it, make sure to add some getter function on that area so that anything that detects it will be able to obtain the prey it belongs to

This was key. I was trying to get the position of that area, but kept receiving [0,0]. If I understand correctly now, this was because the position of that area relative to the root node was [0,0]. I now used get_node(area.get_path()).get_parent() to get the parent of the area and the position from there.

(I strongly assume that there must be something more elegant than get_node(area.get_path()).get_parent(), and I assume the getter function you refer to has something to do with that, but for now - I am happy! Thanks again.)

Mapkinasekinase | 2019-10-30 23:48

get_node(area.get_path()).get_parent() can be replaced by area.get_parent() I suppose :slight_smile:

Zylann | 2019-10-30 23:50