0 votes

I'm making a 3-D flight sim, and the player can fire missiles which are very small and move very fast. There's a big terrain StaticBody in the level which is a concave polygon with lots of valleys to fly through, and I'm trying to make the missile blow up if it hits the terrain. I did the obvious connection of body_entered and area_entered signals with functions in the missile's script that will handle exploding on contact. However, they don't get triggered very reliably. Occasionally the missile will actually explode when it runs into the terrain, but more often than not it just flies through.

For debugging and to see whether this method behaves more reliably than the signals, I put a print("Overlapping bodies: ", get_overlapping_bodies()) statement in the process() function of the missile. The terrain (a StaticBody) will show up on the rare occasions when the missile does explode, but more often the missile will fly into the terrain without it ever showing up on the list of overlapping bodies. Attempting to change the terrain from a StaticBody to an Area and using get_overlapping_areas() didn't help.

I used a similar implementation with the player's plane, and that works more reliably although with very rare occasions when the player can fly into the terrain without exploding. The player's plane is of course larger and slower than the missile.

It seems like the *_entered signals and the get_overlapping_*() methods only trigger if there's an overlap of the borders of the terrain and the missile, but not if the missile is completely within the terrain StaticBody, and my implementation is failing because the missile is so small and fast that it can jump from outside to inside without overlapping the border for a frame.

Anyone know if that's really how it works with concave polygon collisions? And if so, is there a different approach that can reliably detect if an area (or even a point) is within a concave polygon and not just overlapping its boundary? Or do I just need to simplify my terrain a ton and use convex instead of concave polys (which would make the game either much more computationally intense or significantly less impressive and immersive because flying through valleys is fun)?

Godot version 3.2.3 stable mono
in Engine by (26 points)

1 Answer

0 votes

It sounds like your bullets are too fast and small for the collision system to handle them. You could make the ground thicker, the bullets bigger, increase physics fps in the project settings, or try out the other physics engine shipped with Godot.

by (2,482 points)

I had forgotten that there's more than one physics engine. I'll look into that and if it fixes it then I'll reply here for anyone else searching about this issue.

It's definitely not a matter of ground thickness though - it's already mountain size thick. (I made it with Blender's sculpt tools on top of the default cube so it's also very deep.) The missile definitely passes into the interior of the terrain's CollisionShape but isn't triggering the signal or showing up with get_overlapping_bodies()

Changing the physics engine didn't help, but your comment of making the bullets bigger led me to a workaround that seems acceptable. I'm posting it here in case any other devs run into the same issue (or in case I forget how I did it and have to go Googling for an answer when I need to do it again :P)

My missiles are based on an Area root node, but this should also work for StaticBody RigidBody and KinematicBody types. My Missile Area has only one CollisionShape child which is important. (My missile's area also has a child Blast Area node with its own CollisionShape, but having more grandchild CollisionShapes like that is fine as long as the Missile node just has one direct CollisionShape child.) In the CollisionShape: for its Shape property I gave it a new CylinderShape, under Transform I rotated 90 degrees on the x-axis so it's pointing down the z-axis, then back at the Shape property's dropdown I picked Edit, and I adjusted its radius to be appropriate for my missile. You can leave the height at the default of 2 -- it will be changed in code later.

Then in code I have a var collision_shape and in the _ready() function I access the CollisionShape's cylinder shape with

collision_shape = shape_owner_get_shape(0, 0)

It's important to have only one CollisionShape child so you know that calling shape_owner_get_shape and looking at the zero index will work correctly. Then in my _process function, after I handle moving the missile and call orthonormalize() I have

collision_shape.height = delta * speed

where the speed is a variable I created that does pretty much what you would expect and moves the missile along the z-axis that many units per second. The net effect of this is that the CollisionShape ends up being a cylinder that looks "ahead of" and "behind" the missile's current position by half the distance it moves per frame in each direction, so it reliably overlaps with the terrain's boundaries whenever the missile crosses (or is just about to cross) the boundary.

After I have fun blasting the ground for a while I'll get around to making it actually inflict damage.

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.