+1 vote

I'll explain my setup first.

There is an enemy in the game I'm creating that simply runs towards the player; but while it is running, it must check for collisions with other enemies (the player can intersect enemies so they'll hit each other and die) and with the player himself, without ACTUALLY physically colliding (I want it to overlay the player when he reaches it - the enemy is kind of "invencible" while it is running). But after the enemy reaches his final position, it must stop - than I'll treat it as a normal Physical object that the player can't get through.

I thought this would be a simple thing to create by using the Trigger option on the Kinematic Body, as I would just temporarily disable the Physics of the object while it is running but would still get the Collisions triggers; but as I experienced (same problem in https://github.com/godotengine/godot/issues/4393), the Trigger option doesn't work for this exactly. So now I had to create a new Area2D node inside my Enemy scene, in which I would have another CollisionShape2D (the same as the KinematicBody shape), only to test if there is another area intersection with. Is there any other, more optimal way to just temporarily disable the physics of an object while still getting the collision triggers? I don't like the current setup, but I didn't found anything new on the PhysicsBody documentation that I can use.

in Engine by (304 points)

3 Answers

0 votes
Best answer

You could try using a RigidBody set to MODEKINEMATIC. I would expect it to behave exactly like a KinematicBody, but there's a chance that it might receive callbacks like a RigidBody (after all it is a RigidBody, it's just not in MODERIGID).

If that doesn't work I guess there's no other solution but to use an additional Area. Using Physics engines to simulate physically incorrect behaviour is always quite messy, so I wouldn't be surprised if making something move through other objects but still report collisions as if it was colliding is not easy.

by (1,120 points)
selected by

Weirdly enough, I couldn't get myself to move a RigidBody2D in Kinematic Mode. Not sure if it is a bug from the engine or just lack of documentation, but when in Kinematic Mode, integrateforces() won't work (no surprises there), but also I can't use the "move()" usual method of Kinematic Bodies (more or less no surprises there, as the scene is extending from RigidBodies) - but also setlinearvelocity won't work too. Probably I'd have to use set_pos(), but that would defeat the purpose of using the Physics2D engine.

I guess, as it is, there is no way I can achieve what I planned without the more cumbersome solution of adding another Area node, just as you said. But I think I should at least add this as a feature request on GitHub, as this may be useful for other devs.

+1 vote

Try using the Collision layers and collision mask

by (1,198 points)

I tried, but as soon as the collision happens on any end (if the enemy has the same layer as the player mask or the other way around), the physical collision happens.

try disabling it via code or just use 1 layer to enemy-to-enemy collision, one layer to out-of-area collision and one for enemy-to-player collision

The problem is that, the way layers currently work, they have the purpose of choosing in which layer physics collision detection will happen on each object. As soon as a collision is detected, no matter the layer, it WILL trigger the physics response, and the objects will "solidify". If I disable the layer on runtime, I won't be able to test when the scene is not colliding with the player anymore, as the is_colliding() trigger won't happen anymore.

have you tried using an area object as the collision detector of the enemy and using the signal body_enter and his variable(the body's id) so you will be able to make different decision via code by the type of the body

+1 vote

If you want something to trigger on overlap but without the two bodies touching, then I'd recommend using an Area2D node. So here's what I'd try:

Set player to collision layer 0, and mask 1 (so its on layer 0, but collides with layer 1)

Set enemies to collision layer 1 and 2, and mask 2 (so its on layer 1 and 2, but collides with layer 2)

Add an Area2D to the enemies, set its collision layer to nothing, and mask to nothing

When enemy runs, remove its collision layer 1 (though code) and set its Area2D's mask to 0 (so player can't collide with it, and the Area2D can collide with player)

When enemy stops, add its collision layer 1, and remove its Area2D's mask 0 (so player can collide with it, and the Area2D is disabled)

Note that enemies can still collide with each other on layer 2, and the code for setting a collision layer is set_layer_mask_bit(int bit, bool value) and for setting a collision mask it's set_collision_mask_bit(int bit, bool value)

by (846 points)

The problem with that solution is that, when the enemy stops, I want to be able to interact physically with it - but only when it is not moving. So I think I'll have to mash up the Area2D and Kinematic Bodies anyway.

When the enemy stops, it goes back onto layer 1 and disables the Area2D. And since the player's mask is on 1, it can now collide(aka physically interact) with the enemy. So it does work... I think

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.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.