How to Register Knockback Collisions on a Stationary CharacterBody3D

Godot Version

4.2.2

Question

I have a CharacterBody3D that applies a knockback force on collision. However, when a physics object hits it while it’s motionless, the collision isn’t registered.
As I understand it, CharacterBody3Ds aren’t affected by other physics objects.

What’s the best way to achieve this effect?

Should I use a RigidBody3D instead? I prefer CharacterBody3D’s simpler code, except for this collision issue. Should I add an Area3D to the character and use the body entered signal for knockback?

I also use collision.get_normal() to set the knockback direction effectively. Is there an elegant way to do this with the alternatives?

With CharacterBody3d you need to code everything yourself, every physical interaction, which means you can get jank like player being stuck in walls or the floor, so Rigid Body is IMO preferable, even if you need to solve certain corner cases related to it as well.

You may take a look at the little monstrosity I’ve coded for my 3d platformer, though if I knew it existed at the time, I’d try this asset instead first.

I’m relatively new to Godot only been working with it for about 6 months but if you need to do collision with characterbody3D you would need to code it manually. What I would do is probably make an area3D to detect collision

If you’re character isn’t something such as a marble I wouldn’t use a rigidbody3D. If your game is heavily focused on such interactions it might be better to use a rigidbody3D

I found a solution! I don’t know if it’s good practice, and I’m not sure if it has any major performance downsides, but for the mean time it works perfectly.

The solution:
I’ve added a ShapeCast3D as a child of my character, with the target position set to Vector3(0, 0, 0), and set margin to 1 (without a margin, the collisions with other players were laggy at best).

The ShapeCast3D has a method called get_collision_normal(), which allows me to keep my code relatively simple and very similar to what I had.

Here is the before and after of my movement code:

# Before
var collision = move_and_collide(velocity * delta)
if collision:
	knockback_velocity = collision.get_normal() * knockback_force

# After
move_and_collide(velocity * delta)
if %Shield.get_collision_count() > 0:
	knockback_velocity = %Shield.get_collision_normal(0) * knockback_force

I tried using a RigidBody3D, but I couldn’t get the movement to feel quite right. And I actually did stumble on the asset you mentioned, but I didn’t think it was right for me, my character is a spaceship that’s relatively simple, so I don’t need jumping, running and floor detection.

In the end I went with another solution, but thanks for the reply!

Thanks for the suggestion!
That solution is similar to the one I went with in the end. The only difference is that using a ShapeCast3D allows me to use the get_collision_normal() method, so in the end, my code barely has to change.

1 Like

You’re welcome. Hopefully my answer will be useful to someone else who comes upon this thread via google.