How to implement 3D projectile collision?

Godot Version

4.4.1

Question

This question is going to sound so silly considering my ambitions, but how do I implement collisions for projectiles?

That seems like a really simple question, but I have no experience with working with collisions in Godot. So I’ll need some guidance to learn, like I did with raycasts.

I tried watching some tutorial videos, but they didn’t help me understand what I was even doing.

All I want is for the projectile to delete itself when it hits a body. If I learn how to get that implemented, I can figure out a lot more.


So my projectile scene is really simple:

Just a CharacterBody3D parent with some collision shapes and a mesh.


For collisions, do I use the CollisionShape3D node or the Area3D node? I viewed the docs, and it seems like Area3D has all the built-in functions. But I’m not sure.

Let me know how I should implement this. Thanks in advance.

I suggest using RigidBody3D as parent node because it offers easier and more realistic movement and physics. Then, you can just set the Contact Monitor property to true and connect a body_entered signal to a function that runs queue_free().

Quick tip: CollisionShape3D is always paired with another CollisionObject3D, such as Area3D and CharacterBody3D. It gives these nodes a defined collision shape where interactions are calculated.

I need to clarify that this is for a multiplayer PvP game. So relying on non-deterministic physics isn’t a good move. That’s why i used a CharacterBody.

However, please correct me if I’m wong.

You can actually control the movement of a RigidBody3D by using axis locks. If it’s a bullet-type projectile, use apply_central_impulse. For homing projectiles, simply calculate the angle between the target and the projectile, and use apply_torque_impulse to steer it toward the desired angle on the fly. RigidBody3D is actually highly capable and can be as precise as a CharacterBody3D under controlled conditions (I actually used it as a character node once when I got lazy implementing gravity manually).

Take note that using RigidBody3D requires you to understand some fundamental physics concepts, as it relies heavily on your equations and calculations. With good enough physics logic, your projectiles will move smoothly and accurately enough even for a multiplayer PvP game.

1 Like

Man, that sounds complicated. I’ll narrow down my options. But i hope i get another reply with an alternate method soon.

Yeah it’s a bit complicated. If you want to stick with the CharacterBody2D, using an Area3D child will satisfy your collision checks. Just remember to put the correct collision layers and masks on each object.

I use CharacterBody3D/CollisionShape3D for all of my moveable physics objects, but it’s more of a personal preference than anything else. If you look any of my game YouTube videos, you can see the practical effects of that choice. I’m sure I could have done it with Area3D/Collision3D just the same, though.

As far as deleting the projectile when it hits something, it is something like:

func _physics_process(delta: float) -> void:
	var bCollision : bool
        .
        .
        .
	bCollision = move_and_slide()
	if bCollision:
        queue_free()

The code is similar for move_and_collide():

func _physics_process(delta: float) -> void:
	var collision : KinematicCollision3D = move_and_collide(m_vecIncrement * delta * m_nSpeed)
	
	if collision != null:
		var nColliders : int = collision.get_collision_count()
		
		if nColliders != 0:
            queue_free()
1 Like

RigidBody3D would be good if you want physics driven movement, CharacterBody3D would be good if you want to prevent overlaps with other bodies such as walls and players. If you are programming the movement using just an Area3D may be best, it can detect collisions and moves consistently, you can delete the projectile when it hits any body through the body_entered signal, or by checking get_overlapping_bodies. If you need to check for areas, such as projectile vs projectile collisions you can do the same with area_entered and get_overlapping_areas.

3 Likes

I agree it’s a personal preference, but it also can impact performance. In a bullet hell game, you’re going to run into performance problems with less bullets using CharacterBody3D than using an Area3D, because even if you don’t use all those extras settings in CharacterBody3D, they all have to be created before the bullets starts flying. (Pun intended.)

I only mention this for other people coming by this thread later who might be trying to make the decision between CharacterBody3D and Area3D.

Also, pursuant to a discussion about physics performance in another thread on here recently, move_and_slide() and move_and_collide() are black boxes. They are great with dealing with a number of things, but when there are issues, if they are happening inside those functions, you have no visibility into, or control over them.

My personal rule of thumb is if it’s a Character, it uses CharacterBody3D/CharacterBody2D, otherwise I use something else. I define a Character as anything that would be considered a Mob in a MUD (Multi-User Dungeon): Players, Enemies, and NPCs. They are complex enough to benefit from all of those settings.

Things like Projectiles, Blocks, Boxes, Elevators, Switches, etc. I consider to not be Characters and make them something simpler.

5 Likes

Really good point, I’d like to go a step further:

With an Area you will update the position every frame, this isn’t very taxing.

A CharacterBody may use move_and_collide which does a lot with collision calculations, including getting the contact point, normal, depth, etc. and finally it also updates position, so it is always more taxing than an Area.

Then there is move_and_slide which can call the original move_and_collide up to max_slides (usually 6) times. Additionally checking for moving platforms and distinguishing floors, walls, and ceilings. This always calls move_and_collide which always updates position so it is even more taxing than updating an Area.

If you do not benefit from “stopping” collisions against static bodies and only care for the overlap then you should use an Area.

3 Likes

Thanks for all these responses! I believe I know what to try next.

I’m going to change my projectile’s parent node to an Area3D, instead of a CharacterBody3D. Then, update the position every physics frame.

Since Area3D is a child of a Node3D, it has a position and basis I can take from my current implementation easily.

Also, projectiles are both simple and complicated at the same time. They are used 99.9% of the time while playing, but only exist for less than a second. Millions can be spawned in a match, yet only 50 exist at any given time.

I’ll keep you guys updated as I implement things.


Update:

I changed some things around, but now my current implementation of projectile shooting mechanics don’t work anymore. No matter where I shoot, the projectile spawns and stays at 0.0.0.

I’m trying to figure out why, but I’m a bit stumped on that front. I might have to swap back for simplicity.


Update 2:

I changed back to my old implementation, and I think the issue is with the Area3D. It just doesn’t detect anything at all, and I’m currently not sure why.


Update 3:

Apparently, Area3D only reacts to physics bodies. And CSGboxes AREN’T physics bodies. I used CSGboxes to build my maps because they’re easy to work with. Maybe Area3D isn’t the way to go for projectiles.


Update 4:

Apparently, move_and_collide() HAS NO DOCUMENTATION. NONE! I do not know how to use this function effectively without it. I could use some help with that.

Whoops, my bad. It DOES have documentation. Just in the PhysicsBody3D page.


Update 5:

So, move_and_collide() might be the play, as it stops the projectile on bodies.

But are CSGshapes bodies?

CSGBox3Ds Are your problem. The docs state for all CSGShape3Ds (which is what Box inherits from):

To solve your problem, you really want to export your boxes as gltf and re-import them as gltf which will then be a MeshInstance3D which then can be used with a StatiBody3D. If you don’t, you’re going to start running into performance problems pretty soon. You’ll also then be able to use an Area3D for collisions again.

1 Like

Wow. That’s really good to know. Once I bake CSGshapes, I can use them for collision.

@dragonforge-dev Although, how do I bake ALL of the CSGshapes? Do I group them all into one node? Or rebuild my test map without CSGshapes?

If I have to rebuild my test map, what’s an easy way to create geometry for maps? I don’t know how to use Blender at all. I’ll learn it if I have to.

I use a lot of CSG shapes for prototyping too, Area3D can detect them as bodies (through body_entered, but their type will not be a physics body that’s why it emits a Node3D), but I do agree that they are weird. I don’t think you’ll have to rebuild your map, or create any particularly funky out of place code to allow CSG collisions.

Still, in 4.4.1 a new feature is baking CSG shapes to MeshInstance3D and collision shapes with the tool bar.

2 Likes

That’s fantastic! I know where to start next tomorrow.

1 Like

On the Blender route, I highly recommend the GameDev.tv Complete Blender Creator 3 Course. I found it super informative, and it teaches you how to create a fully modular dungeon. Which the skills learned can be used for any kind of 3D level building.

As for how much to break it down, I’d recommend you break your objects into different physics materials if you’re using any. Otherwise, I’d look at making things modular. If you can repeat the same piece multiple times, it reduces the load on RAM.

1 Like

Learning Blender/3D modeling in general has always been something on the backburner for me.

The reason it’s like this is because I don’t want the pressure of learning a second craft. (I vividly remember trying to pick up 2D illustration multiple times, but failing) The opportunity cost is CRAZY expensive.

But, there’s a small artist in me that dreams of creating stupid stuff. However, the more mechanical design/programmer foundations never break enough for me to commit.

Edit:

This isn’t related to the topic, but I just wanted to emphasize how different game design is as an art.

Illustration produces something you can see.

Game design produces something you can do.

It’s like building a machine for others to use. Meaning, you can’t effectively judge if someone is a good game designer by looks alone. Their work must be played, experienced, and used; like a machine.

You also iterate like a builder. Swapping out different parts, running tests, collecting feedback, and perform maintenance.

I’m much more of a game designer/programmer (If you couldn’t already tell). It’s so much more mechanical than creative. But, that’s genuinely how my brain works.

You’ve mentioned that before. Thing is, if you’re building with CSG boxes, building with Blender isn’t that difficult. The course I recommended is usually on sale for like $13 or so, The guy who teaches it is a really good teacher, and he starts you out with all the basics of learning the tool so you never feel lost. Even if you aren’t immediately proficient with it and decide not to do it, you will gain an appreciation for how it’s done and knowledge that will help make you a better game designer. Because when you need to communicate with someone else who is good at it, you will have a better vocabulary for communicating with them.

I also personally like courses because they are like a working vacation. They’re a way to take time “off”, while still learning something relevant.

1 Like

So, I tried out CSGshape baking, but the projectile’s Area3D still didn’t detect it.

I tested with with StaticBody3D and it didn’t work either. Body detection only works with RigidBody3D for whatever reason.

Bummer.