EDIT:
There’s been lots of varying discussions/questions/solutions in this thread over the last year, so I’m gonna try to sum everything up:
Making character controllers in Unity is currently very frustrating. We do not have access to what we need in order to make “perfect” kinematic character controllers. Nvidia explains better than me why non-kinematic rigidbody character controllers have problems: http://docs.nvidia.com/gameworks/co…ntrollers.html#kinematic-character-controller
Basically, we need access to methods that would allow us to probe the environment for nearby colliders and resolve the collisions by ourselves, with custom code. In other words; we need to be able to collect collision information and THEN apply movement to the rigidbody. All in the same fixed update. Currently, this is impossible in Unity, because collision info can only be accessed through OnCollisionX(), which always occurs after the FixedUpdate, so any attempts to react to collision information will always occur in the next physics update, with a noticeable delay.
What we need is access to PhysX’s “computePenetration” method (see “Penetration depth” section): Geometry Queries — NVIDIA PhysX SDK 3.4.0 Documentation (thanks to EnokStenhuggare for the link). So our hypothetical Character Controller would be a kinematic rigidbody and would do something like this in its FixedUpdate():
- Collect all nearby colliders using OverlapCapsule
- Test penetration against each of these colliders with “computePenetration”
- Manually resolve collisions using the computed penetration information of step 2
- Use penetration information of step 2 to also determine things like “grounding” status, slope angles, obstruction normals, snapping to ground surface, etc… (Normally I’d use a downward spherecast with a small upwards offset for this, but now we can hit two birds with one stone)
- Do regular character movement handling (Move the kinematic rigidbody at a certain velocity with a sweep test to preemptively detect movement collisions, etc…)
The manual collision detection step is necessary because not all collisions are a result of the character’s movement. Some collisions can happen when another collider moves against a stationary character, in which case sweep tests are of no use to us. It would serve a very similar purpose to the “enableOverlapRecovery” option that was added to the default CharacterController component recently. Sweep tests are also unreliable if we are already colliding with a surface, and moving against that surface. All this will allow us to remain in full control of how our character is affected by external forces/collisions, and how it affects other rigidbodies
Here is the feedback page to vote on the issue: https://feedback.unity3d.com/suggestions/expose-more-of-the-physx-api-namely-penetration-depth
Original post text (You dont need to read it)
We need a way to detect collision hits manually so that we can use it on Update(), instead of FixedUpdate() / OnCollisionStay(), etc…
Here is why I think this would be important:
I am working on a custom character controller, and I want it to be as perfect as possible. I think a CC that feels perfectly fluid and responsive is key to a game’s success (depending on the type of game, of course). Due to the way Unity works, character movement typically has to be done in FixedUpdate(), because that’s when collisions are processed.
Now consider the following:
- Update() happens before every drawn frame
- FixedUpdate() and OnCollisionX() happen at fixed time intervals
Conclusion: You cannot guarantee that you will collect collision hit information before every frame that is drawn. Therefore, you cannot process your character movement logic / ground detection before every frame. This results in undesirable choppy movement. It is hard to notice at first, but compare an object moving in Update() and one moving in FixedUpdate() at high FPS and you will easily notice it. There will be times when two Updates() happen in succession without a FixedUpdate() in-between, and when that happens, there will be a frame where the character will not have moved.
Here’s the console output of a simple test I made to confirm this :
In this test, an object moves on FixedUpdate(). FixedUpdate Debug.Logs itself in the console, and so does Update (with the position difference since last update). You can clearly see that on most frames, the character doesn’t actually move, because the FPS is higher than the FixedUpdate rate.
Failed solution attempts:
Here are the attempts I have made to try and detect collision hits on Update and/or mimmic a controller that runs on Update():
- A disgusting bunch of Raycast() all around my character: Just kidding, I will never fall that low!
- CapsuleCast(): This only works in the direction it is cast, so I’d need to do maybe 6-10 of these casts per frame in all directions, which is ugly, inefficient and unreliable.
- OverlapSphere(), or even the hypothetical OverlapCapsule(): These return only the hit Collider, but not the contact point or normal of the collision. To use these functions, I would have to write my own collision detection code between my character’s capsule and every other collider type in Unity, including mesh colliders. This doesn’t seem like a good time, and it is the kind of functionnality that you’d expect to be already implemented in an engine. A very brave and determined individual actually went through the trouble of doing this, but this solution can only check spheres, and requires manual mesh collider preparation: Custom Character Controller in Unity: Release 2.0.0 | Roystan Ross
- Detecting environment hits with OnCollisionEnter/Stay, but moving on Update: This creates a weird desync between movement and collision detection that results in a few frames of going through things when landing on the ground and ramming into a wall for example. Not very ideal.
- Setting the fixedDeltaTime to 144hz: This solution guarantees that your monitor will never be able to display frames faster than your character’s movement is processed. It may sound like the perfect solution at first, but the unfortunate downside of it is that the physics simulation becomes more costly. I’m a huge fan of playing at 144 FPS, but I really don’t need all the physics simulation in my game to run at such a high frequency. I only need my character controller to do that.
- Using “Interpolate” on the rigidbody: This is very close to being a perfect solution, but interpolate works on the rigidbody’s velocity. If you want your character to be kinematic like I do, moving it through velocity is not an option. It has to be moved directly with its position, and Interpolate doesn’t work in that case. (Right now, to achieve the “kinematic” effect while still being able to detect collisions, I apply all position and rotation constraints on my rigidbody, and set kinematic to false). On top of that, this becomes a problem in cases where you want your character to “stick” to the ground when going up a slope and arriving at a plateau. You will see your character make a little ‘jump’ at the top of the slope if you use interpolation. This becomes very noticeable in first person.
Bottom Line:
I think Unity would benefit immensely from getting CheckSphereHits(), CheckCapsuleHits(), CheckBoxHits(), and CheckMeshHits() functions that return an array of Collision or ContactPoint. These functions would allow us to collect collision hit information before every drawn frame, and therefore make great character controllers, among many other things. Basically our character would become a kinematic capsule rigidbody whose movement and manual collision response would be processed on Update(), making the rest of the world collide with it normally, but giving us complete control on its movement.
If anyone has some kind of solution to this, please share. I also invite you to correct me if I’m wrong about the inner workings of Update() and FixedUpdate()
Here is the Unity Feedback post if you want to vote for it: https://feedback.unity3d.com/suggestions/additional-raycasting-slash-collision-functions
Thanks