Has_overlapping_bodies doesn't see code-generated collisionPolys?

Godot Version

Godot 4.2.2 stable

Question

I’m trying to set up an Area2D as a child of a character, made to detect whether or not it collides with any CollisionPolygon2D in the tree. Terrain’s CollisionPolygon2D need to be dynamic in-game, as part of the game will be adding to and removing from polygon2D at runtime. The scene tree looks like this:

┖╴Main
┠╴Character
┃ ┠╴Sprite2D
┃ ┠╴CollisionShape2D
┃ ┖╴testArea2D # ← area we’re testing has_colliding_bodies with
┃ ┠╴CollisionShape2D
┠╴TerrainStaticBody2D
┃ ┠╴TerrainPolygon2D # ← generates CollisionPolygon2D sibling based on itself in script

If I run has_overlapping_bodies() from Character’s script to see if testArea2D has any overlapping bodies, it will always return false. However, if I manually add a CollisionPolygon2D for TerrainPolygon2D (not generating it through code), it returns true during overlaps as expected.

If I debug and run print_tree_pretty(), it shows ┖╴@CollisionPolygon2D@11 as a child of TerrainStaticBody2D as expected, but overlaps will not show. Why are these different? Is there something I need to do in code to make it so has_overlapping_bodies() can see CollisionPolygons generated by script?

(Relevant code for generating/adding the CollisionPolygon2D)

	collisionPolygons = CollisionPolygon2D.new()
	add_sibling.call_deferred(collisionPolygons)

Also, everything is just on collision layer/mask 1 for the moment while I figure this out, so it shouldn’t be that.

Area2D.has_overlapping_bodies() is only updated once per physics step. If you add the CollisionPolygon2D after it won’t be detected until the next physics step.

Also, the code you posted adding/generating the CollisionPolygon2D does not have any polygon. Make sure you are giving it a polygon

The next line is a call to an update() function, which updates collisionPolygons.polygon, so that’s covered! Good callout, though.

As for it only being updated once per physics step, I’ve tried working with that in mind, and that doesn’t seem to be the issue?

	$testArea2D.position = get_global_mouse_position()
	await get_tree().physics_frame
	if $testArea2D.has_overlapping_bodies() : $testArea2D/RichTextLabel.text = "True"
	else : $testArea2D/RichTextLabel.text = "false"

This is some debug code I’m running in Main’s _process(_delta). It sticks a small Area2D (with a small collision polygon child) to the mouse’s position every frame, and lets me test if the terrain overlaps any bodies. As mentioned before, it’ll never read ‘true’ if I’ve got the collisionPolygons generated through a script in the terrainPolygon’s ready() function, but will read ‘true’ as expected if I manually create the collisionPolygons in the editor, and I don’t understand why they’re different.

I don’t know then. Something isn’t correctly set.

I tested this:

extends Node2D


@onready var static_body_2d: StaticBody2D = $StaticBody2D
@onready var polygon_2d: Polygon2D = $StaticBody2D/Polygon2D
@onready var area_2d: Area2D = $Area2D
@onready var label: Label = $Area2D/Label


func _ready() -> void:
	var collision_polygon_2d = CollisionPolygon2D.new()
	static_body_2d.add_child(collision_polygon_2d)
	collision_polygon_2d.polygon = polygon_2d.polygon.duplicate()


func _physics_process(delta: float) -> void:
	area_2d.global_position = get_global_mouse_position()
	if area_2d.has_overlapping_bodies():
		label.text = "Overlapping"
	else:
		label.text = "Nope"

With this scene:

image

And it’s working fine:

1 Like

Three things I would check.
Turn on visible collision shapes and see if you have a polygon (make sure your points are legit)
Next double check that you are adding the CollisionShape2D as a child of a collidable body (it is being added to where it should be)
Finally check the REMOTE tab and click on your dynamically added CollisionShape2D then look at the polygon points in the inspector to make sure they are valid.
Remember that polygon points are Vector2 points so the array must look something like this:

var pva:PackedVector2Array = [
		Vector2(200,10),Vector2(400,10),Vector2(400,200), Vector2(200,200)
	]
1 Like

This is basically exactly my setup. Thanks for putting this together, I’ll pore over this and see what I did wrong!

Update - can’t figure it out still. Ran the exact same test as above and got the same results (it works). Dunno what I screwed up in my actual project, but I’ll dig it out.

Update2 - Found it! I was calling the collisionPolygon2D’s polygon to update outside of a physics frame, which apparently made it invisible to has_overlapping_areas(). I’m not smart enough to understand WHY that is, but making sure it updates the polygon’s vectors during a physics frame process (either during _physics_process or via await get_tree().physics_frame ) solves the issue and makes it work as expected.