+1 vote

Hallo everyone! So, I am trying hard to make an Area2D check on an explosion of a projectile.
I would love to create a "utils" func to call from signleton for all the possible explosions, so I did create a func in an Autoload script called explosion(impactpos, radius)
The func will be called by another node which will be queued
free on impact. So far I have the following not working at all:

(in utils.gd, Autoloaded)
func explosion(impact_pos, radius):
    var area = Area2D.new()
    var shape = CircleShape2D.new()
    shape.set_radius(radius)
    area.add_shape(shape)

    base_node().add_child(area)
    area.set_pos(impact_pos)
    area.set_enable_monitoring(true)

    var body_list = area.get_overlapping_bodies()
    print(body_list)
    for i in body_list:
        if i.has_method("get_explosion"): 
            i.get_explosion()
    area.queue_free()

note: basenode() is a func in utils which calls the base node from getroot().gettree().getchild(-1)

With this code, body_list is always empty.

Additional infos: I have used the method .getoverlappingbodies() successfully for other purpouses. Attached to a node with an editor-created Area2D + CollisionShape with exactely the same bodies (which are rigid bodies), I used this method outside _process and was working as charm. No Area2D.new() and no "area2d.tscn".instance(), so I guess the issue should be here somehow.

Any guess?

Updates: I have also tried to attach this func in the script (and node) which is calling it. No luck.

Plus: I have also tried to create an explosion.tscn external scene, which has an Area2D as base node and an editor-created CollisionShape. So instead of using:

area = Area2D.new()

i called for the instanced scene:

area = load("res://explosion.tscn").instance()

No luck either way. The body_list is still always empty even if I can finally see the Area2D on "enable visible collision" mode.

asked Oct 19, 2017 in Engine by iRad (16 points)

2 Answers

+2 votes
Best answer

After some experimentation, it seems the problem is that the Area2D isn't quite ready to be used yet. If you yield for two fixed frames, one after setting up the Area2D, one after moving, it begins to report overlapping bodies properly.

func explosion(impact_pos, radius):
    var area = Area2D.new()
    var shape = CircleShape2D.new()
    shape.set_radius(radius)
    area.add_shape(shape)

    base_node().add_child(area)    
    area.set_enable_monitoring(true)
    yield(get_tree(), "fixed_frame")

    area.set_pos(impact_pos)
    yield(get_tree(), "fixed_frame")

    var body_list = area.get_overlapping_bodies()
    print(body_list)
    for i in body_list:
        if i.has_method("get_explosion"): 
            i.get_explosion()
    area.queue_free()
answered Oct 19, 2017 by aozasori (462 points)
selected Oct 19, 2017 by iRad
0 votes

I followed along the eon's advice on the Discord channel when you asked that question there. After having some time and fiddling around this problem, here's how I managed to get immediate overlap information detecting bodies and shapes using Physics2DServer:

# Define any shape
var check_shape = ConvexPolygonShape2D.new()
check_shape.points = points

# Get space and state of the subject body
var space = Physics2DServer.body_get_space(body.get_rid())
var state = Physics2DServer.space_get_direct_state(space)

# Setup shape query parameters
var params = Physics2DShapeQueryParameters.new()
params.set_shape_rid(check_shape.get_rid())
# Set other parameters if needed
# params.set_object_type_mask(Physics2DDirectSpaceState.TYPE_MASK_COLLISION)
# params.set_transform(Transform2D(0, offset))

var result = state.intersect_shape(params)

The result is an array of dictionaries containing all the needed overlap information. Check documentation for more details

Notice that you only need to define a shape without having to create Area2D nodes.

Thanks for asking the question!

answered Nov 8, 2017 by Xrayez (1,276 points)

I know this is an old post but I wanna know where points variable come from and what do it have?

points is any convex polygon which is represented as a PoolVector2Array with vertices defined in counter-clockwise order to create an actual ConvexPolygonShape2D. You can create a more simple shape for this purpose, for instance:

var check_shape = CircleShape2D.new()
check_shape.radius = 32

Regarding this piece of code:

params.set_transform(Transform2D(0, offset))

offset which is Vector2 is used to displace the defined shape so that it's properly checked against collision bodies that are present within physics space in global coordinates. 0 is for rotation, which is not needed for circle shape, for instance.

thanks for this follow-up @Xrayez.

I know it's old, but i'm trying to understand what's happening here:

var space = Physics2DServer.body_get_space(body.get_rid())

specifically, what kind of thing might the body variable be holding in a minimal godot project?

That's usually any PhysicsBody2D derived node: StaticBody2D, RigidBody2D, KinematicBody2D etc. get_rid is present in CollisionBody2D.

For checking an overlap of physics bodies, I'm currently using a ShapeCast2D node btw, see proposal: https://github.com/godotengine/godot-proposals/issues/710

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.