Calling to all bodies in an Area2D?

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

Hey gang, basically im trying to do something to ALL the bodies present in an Area2D when it starts monitoring. (It’s a mortar in a vehicular combat game)

My code is the following:

func _on_Area2D_body_entered(body):

	if !exploded: #This one is to prevent some bugs

		for bodies in $Area2D.get_overlapping_bodies(): #This one SHOULD get all the bodies in the area.

			if body.is_in_group("vehicle"): #If that body is a vehicle...

				body.call("gotshot", 35) #Damage it!

		exploded = true #And such was the life of the mortar round.

Problem is, it’s only damaging ONE vehicle. What’s wrong? (Both vehicles have the “gotshot” function, they are literal duplicates)

:bust_in_silhouette: Reply From: Wakatta

Because you’re using the body entered _on_Area2D_body_entered(body): and not bodies in for bodies in $Area2D.get_overlapping_bodies(): from which you’re iterating over so the newly entered body is actually getting damaged by the number of bodies in the Area2d

I understood what you said but couldnt write a working code, would you mind giving me an example?

Tato64 | 2020-12-09 02:08

Like Adam_S points out with his example.
You just need to change body to bodies after your for loop

However i’d also suggest you used better naming conventions to avoid future such problems by having a specific keyword for loops like:

func _on_Area2D_body_entered(body):

if !exploded: #This one is to prevent some bugs

    for entity in $Area2D.get_overlapping_bodies(): #This one SHOULD get all the bodies in the area.

        if entity.is_in_group("vehicle"): #If that body is a vehicle...

            entity.call("gotshot", 35) #Damage it!

    exploded = true #And such was the life of the mortar round.

Wakatta | 2020-12-09 02:27

Copied and pasted my other reply:

I changed it to that, it looks like this now:

if !exploded:
    for bodies in $Area2D.get_overlapping_bodies():
        if bodies.is_in_group("vehicle"):
            bodies.call("gotshot", 35)
        print(bodies)
    exploded = true

But it’s still not working :S
I added a print to see if it’s event detecting both cars im using to test, but it isnt.

Tato64 | 2020-12-09 02:46

:bust_in_silhouette: Reply From: Adam_S

I think your code should be like

if bodies.is_in_group("vehicle"):
    bodies.call("gotshot", 35)

Instead of

if body.is_in_group("vehicle"):
    body.call("gotshot", 35)

I changed it to that, it looks like this now:

if !exploded:
	for bodies in $Area2D.get_overlapping_bodies():
		if bodies.is_in_group("vehicle"):
			bodies.call("gotshot", 35)
		print(bodies)
	exploded = true

But it’s still not working :S
I added a print to see if it’s event detecting both cars im using to test, but it isnt.

Tato64 | 2020-12-09 02:37

I did a quick test and it seems like when _on_Area2D_body_entered() gets called for the first vehicle, get_overlapping_bodies() only contains one of your vehicles, and when it gets called for the other vehicles your exploded variable is true and your code will not be executed.

You can see this when just printing the overlapping bodies

func _on_Area2D_body_entered(body):
    print($Area2D.get_overlapping_bodies())

If that’s really the case (not sure, because I’m tired) and you don’t solve this, I will look at this tomorrow.

Adam_S | 2020-12-09 03:15

Hmmm… it prints:

[[KinematicBody2D:1731]]
[[KinematicBody2D:1731], [KinematicBody2D:1767]]

Does this mean it’s detecting one twice?

Edit:
This code works almost perfectly:

func _on_Area2D_body_entered(body):
	for bodies in $Area2D.get_overlapping_bodies():
		if bodies.is_in_group("vehicle"):
			bodies.call("gotshot", 35)
	$Area2D.queue_free()

BUT, it is in fact detecting (and damaging) one of the cars twice

Tato64 | 2020-12-09 03:46

It means that _on_Area2D_body_entered(body) is called twice, but at the first time only one vehicle is in get_overlapping_bodies() and your exploded variable gets true.
So your code won’t be executed for the other vehicles.

Adam_S | 2020-12-09 03:52

Oh my god, you’re right, how could i be so blind lol
Its called on body entered and there’s 2 bodies in there! :facepalm:

Ok, so… the problem lies in _on_Area2D_body_entered(body)

I tried removing it and putting the code somewhere else, but so far it isnt working as intended.

I think i’ll just make a new node for the Area2D that does all this on “ready” and instance it inside my mortar.

But tomorrow, because it’s 3AM here and i have spent way too much time trying to figure this out.

Thanks for the insight!

Tato64 | 2020-12-09 05:53

:bust_in_silhouette: Reply From: Tato64

SOLVED

First of all, thanks to everyone that commented, all your insight lead me to the solution.

Basically, i belive the fact that my explosion lasts for only 1 frame was adding some issues. Also, calling it on _on_Area2D_body_entered(body)was incorrect, since it would be called as many times as there were cars inside the Area2D.

This was solved by making “explode” its on function, triggered in _process (Wont work on _ready).

The following code works perfectly:

(This code belongs to the Area2D itself, not the mortar node)

func _process(delta):
	if !exploded:
		explode()

func explode():
	print(get_overlapping_bodies())
	for bodies in get_overlapping_bodies():
		if bodies.is_in_group("vehicle"):
			bodies.call("gotshot", 35)
		exploded = true #This HAS to be in this indentation level, if it's in the previous one it doesnt work.

I’m glad you figured it out as i too have stayed up to 3AM debugging code
So you are understood. Note this though “It’s better to take a rest and approach with fresh eyes”

Wakatta | 2020-12-09 21:39