How to detect collision before physics is applied?

Godot Version

4.2

Question

What I want to achieve:
Having two bodies (one called “Projectile”, one “Enemy”, both RigidBodies with a sphere shape) rolling around, colliding with the static environment.
But once both bodies touch, the “Enemy” ball should disappear before it impacts the “Projectile” body.

What I tried first:
I first tried to work with RigidBody’s body_entered signal, freeing “Enemy” in there. But it seems this method is only called after physics has been processed. This results in “Enemy” being removed but “Projectile” changing its way due to the collision.

Where I am stuck now:
Since there is nothing similar to before_body_entered in which I could handle the collision before physics is applied (which should really exist in Godot from my POV), I tried adding a “DetectionArea” with a detection “DetectionShape” that is slightly bigger the the body’s shape (the rigidbody is 1m in diameter while the detection area is 1.2m in diameter).
This now looks the following:

image

Now the crazy thing: Even though the detection area is larger than the rigid body, sometimes it happens that the rigidbody is hit before the area detects the enemy body. I tested this with the following code where I register both kinds of enters, enters into the detection area and enters into the rigidbody.

func _on_detection_area_body_entered(body):
	print("> body within detection area: ", body.name)
	if "Enemy" in body.name:
		var enemy = body as RigidBody3D
		enemy.queue_free() # also enemy.free() does not work
		enemy.free()
		print(">>> body was enemy, destroyed")


func _on_body_entered(body):
	print("> body collided: ", body.name)
	if "Enemy" in body.name:
		print(">>> body was enemy")

This sometimes leads to the following output (where “body collided” is printed for the rigidbody before “body within detection area” is printed for the Area3D):

  • body collided: EnemyBody
    — body was enemy
  • body within detection area: EnemyBody
    — body was enemy, destroyed

TL;DR:
How can I reliably detect that a collision is going to happen before physics is applied?
If an additional Area3D larger the corresponding RigidBody is needed, how can I make sure that the area entering is detected before the collision?

Thank you very much!

Best,
Markus

You might be in need of the PhysicsServer: PhysicsServer3D — Godot Engine (stable) documentation in English

1 Like

Thank you for your answer. But even though it probably might help me, I have to admit that a single-line answer is also most probably not going to help me.

How could the PhysicsServer3D help me regarding my problem that the larger detection area is triggered later than the smaller rigid body?
This seems more like a flaw in Godot’s engine rather than something I could solve “hacking” into the physics system?

Best, Markus

Maybe change their collision layers. If the world is layer 1, projectile is layer 2, enemy is layer 3. Then projectile could mask layer 1 only, and the enemy could mask layer 1 and 2. This way the projectile will ignore the enemy and continue its motion, but the enemy would detect any collision with the projectile, then have the enemy free itself if it collides with a body in layer 2.

Otherwise casting via a ShapeCast3D node or in code with PhysicsDirectSpaceState3D could be used to search the area ahead of the projectile, but the oddball cases where the enemy’s path crosses on the exact frame of collision would be a potential issue.

2 Likes

The right way to do this is a combination of collision layers and area.

In summary, what you want is to let the enemy rigid body detect a projectile without the projectile affecting it. This is exactly what an area is used for.

With your current setup, make sure either the enemy rigid body or projectile rigid body masks the collision layer of the other so they won’t detect each other. Once that’s done, you can use the area of the enemy to detect projectile and do anything you want once it’s detected.

2 Likes

Thank you both!

You are correct, I was able to solve this with collision layers and collision masks.

It is really sad that the documentation does not contain a useful example for the more complex use cases of this feature - the first sentence in the documentation “One of the most powerful, but frequently misunderstood, collision features is the collision layer system.” is pretty much due to this fact.

To summarize what I have do to make everything work:

  • World: StaticBody objects (in layer 1, mask does not matter (set to all))
  • Enemy: RigidBody objects (in layer 2, mask 1 (only collide with world))
  • Projectile: RigidBody objects (in layer 3, mask 1 (only collide with world)) with additional Area3D (no layer (cannot be detected by others), mask 2 (only detects enemies))

This way, RigidBody movement is based on the world, and projectile can detect enemy touches using the Area3D.

Thank you all for you help.

Best, Markus