Continuous Collision detection produces ghost collisions

Hello :slight_smile:

After going back and forth on whether to use CharacterController or Rigidbody a couple times already, we have to move back to Rigidbody again due to some new technical requirements.
Because our character is rather fast-moving (can only reposition using a dash) we decided to use Continuous Dynamic collision detection on it.

This detection mode, however, produces rather weird results. It produces ghost collisions in a way that looks like they’re caused by the Physics calculation assuming a faster movement than is actually happening or assuming a larger collider than is actually used. This means the character is no longer able to “fit through” places that it should easily be able to pass.

Has anyone encountered something like this before?

I’ve attached a video that illustrates this behavior with Continuous, Continuous Dynamic and Continuous Speculative detection. Using Discrete does not cause the problem, but skips collisions, which is precisely what we’re trying to avoid. (you can see the rigidbody and collider settings as well as the fixed time step in the video)

Some more info on this:

  • the rigidbody is moved using Rigidbody.MovePosition()
  • the behavior remains the same when reducing the capsule radius to 0
  • different interpolation settings do not change the behavior on any collision detection mode

Some code:


I can provide the code in text form if preferred, but it’s really nothing complicated to get stuck on. :smiley:

I believe the 3D version of MovePosition is always using the discrete collision detection mode and teleports the rigidbody to its destination and so if your player is moving quickly then it’ll step over any objects without being detected.

I suggest disabling isKinematic and switching to AddForce to move your player to its destination.

If you don’t like using a non kinematic rigidbody because you want to have more control over the collision response then you can use modifiable contacts.

Or if you want to use MovePosition then you could do a rigidbody SweepTest along each step of the path.

Thanks for the pointers. I’ll check out modifiable contacts. :slight_smile:
Edit: To be honest, though, this really shouldn’t be necessary. No collision detection mode should produce these kinds of erroneous collisions to begin with. It is absolutely possible this is due to user error, but I don’t see how.

I want to stick to kinematic rigidbody because our intended behavior would be quite difficult to achieve much less control using AddForce et al.

MovePosition with a kinematic rigidbody should not be teleporting, though. As per the (admittedly still quite self-contradictory) documentation it “Moves the kinematic Rigidbody towards position.”

First you should check what the character is colliding with, in terms of contact normal, contact position, object name, etc.
Try to receive OnCollisionEnter and OnCollisionStay events and show them in the log or gizmo.

Also, SpeculativeCCD can theoretically cause ghost collisions.

From another perspective, if you just want to prevent tunneling, you do not necessarily need to use RigidBody; you can detect collisions with CapsuleCast, for example.
(Note that CapsuleCast cannot detect objects that are in contact at the start. So if you use CapsuleCast, I recommend that you double-check with a normal-sized capsule and a smaller one).

MovePosition doesn’t work like the CharacterController’s move. MovePosition instantly teleports the rigidbody to the destination but it will interpolate the movement visually but not physically. Whereas setting rigidbody.position will teleport the rigidbody to the destination without any interpolation.

You can verify this by using MovePosition to instantly move a rigidbody capsule 10 meters with a wall between the source and destination position. The rigidbody won’t be blocked. Yet if you tried this with a CharacterController it would hit the wall and stop.

I believe the 2D version of MovePosition also works like a CharacterController.

I logged all that. It is colliding with the correct colliders, just at distances and angles that don’t coincide with either collision detection Continuity the way it’s set up or the actual velocity/bounds of the player collider.
Also, we are not using speculative mode. :slight_smile:

We need Continuous Dynamic collision for technical reasons to do with the damage system, unfortunately. It’s a bit of a long story to get into here, but I already set up a system that checks along the movement for any skipped collisions (or hits) using a “dragged” capsule cast. Unfortunately, this doesn’t suffice so that leaves us with having to re-incorporate the Unity physics again.

Hm. You are contradicting the documentation a bit so I don’t know what to believe.
I’ve googled this a number of times before and everybody seems to have a different take on this. :frowning_face:

What would you say is the best way to prevent skipped collisions between a fast-moving player and slow-moving enemy entities using a CharacterController?
As I wrote above, I’ve already implemented a “dragged” capsule sweep behind the player to pick up any missed hits, but I still need the Unity collisions for some cases and want to improve their responsiveness. :thinking:

I also understand that MovePosition does not do any physical interpolation.

Yeah it’s confusing. And in your case you’re using a kinematic rigidbody and so it won’t be blocked regardless. Although I’m guessing you’re trying to stop the player with your ExecuteHits function?.

I also see that you’re using ‘Enable Kinematic Pairs’ in the Physics settings which does seem to improve the collision detection when using MovePosition. So perhaps your issue is with how you’re handling the collision with ExecuteHits…

The collisions with static colliders are actually not part of ExecuteHits. They are caught in the PlayerMovement script and sent to the current active move (f.e. Dash). Every move can define its individual handling of collisions or just sent back that the player should be staggered. In this case, collisions with static colliders while dashing cancel the current move and start the Stagger move (which just takes the last velocity of the player, calculates a resulting stagger velocity and sends the player in the direction reflected from the collision normal).

TL;DR:
Collisions are handled by Unity Physics in the video. Nothing really special happening here. :thinking:

I am quite sure this behavior is not due to anything particular I am doing. If you break it down, I lerp a kinematic rigidbody from point A to B, trying to catch all Kinematic-Static collision pairs along the path using Rigidbody.MovePosition() with Continuous Dynamic collision detection.

I’ll get back to it tomorrow. Test around with frame-stepping my Dash.

Yes, sleep! You’ll see the issue much better in the morning! :slight_smile:

Hmmm, it seems that Unity internally changes the collision detection method to SpeculativeCCD when it is set to continuous for kinematic rigid bodies.

I confirmed this behavior in the latest Unity6, which is clearly SpeculativeCCD.
Sorry, I didn’t know that either.

Its probably got something to do with how your environment is pieced together and the geometry is not seamlessly integrated.