Note: This is a pretty long drawn rant
I've been battling with the Physics API exposed by Unity on and off for months now, mostly trying to create high quality character controllers for third and first person games. There are basically two ways to create a character controller, you either use a full rigidbody and try to control the volatility of it or you use a kinematic rigidbody and move it manually using translates and add features (like collision) that you don't have.
Now, these both options both have very specific problems. First I want to talk about using a full rigidbody and trying to constrain it to behave properly. This was the first route that I took for both my character controllers. The main problem with using a rigidbody for your character controller is that the movement precision is constrained to your physics update rate, this is not the same as your frame rate. This will lead to movement that looks "choppy" (for lack of a better word), you can clearly see the difference if you create a cube and first move it inside Update and then move it in FixedUpdate and compare the two.
There are other problems with using a physics driven rigidbody also, such that it's very hard to get 100% precise movement (something which is required when building a controller for a multiplayer FPS). It's also incredibly hard to re-position, correct or re-play movement or movement commands when using a rigidbody, something that is also required for many types of multiplayer games.
So, with all these problem associated with a physics driven rigidbody, I ended up going with the more manual route: Using a kinematic rigidbody and manually move and position it and detect collisions manually. Moving and positioning the body is obviously very easy, as it's just modifying the transform.position property. However, properly detecting collisions and making sure that the character doesn't go through any object, as it turns out, is really really hard to get right inside Unity.
The common way to manually implement collision detection for a player character is pretty simple:
- Use a capsule as the players body
- Define what's referred to as a "skin width"
When the players capsule intersects another collider, you perform a push-back by applying a vector which is inverse to the collision, both in terms of magnitude and direction. If the intersecting collider has gone more then the width of the skin into the players capsule movement is halted completely.
Now, this would be really easy to implement if the proper methods were exposed by Unity, but they are not. You have access to these methods:
- Physics.CapsuleCast - Can't use (only returns one hit)
- Physics.CheckCapsule - Can't use (only returns a bool)
- Physics.SphereCast - Can't use (only returns one hit)
- Physics.CheckSphere - Can't use (only returns a bool)
- Physics.Raycast - Can't use (doesn't represent any width)
- Physics.RaycastAll - Can't use (doesn't represent any width)
All methods other then CapsuleCastAll, SphereCastAll and OverlapSphere are essentially useless for this purpose, as they all have some limitations on the return value. The obvious choice should be CapsuleCastAll, and the manual for it even states: "... is useful when a Raycast does not give enough precision, because you want to find out if an object of a specific size, such as a character, will be able to move somewhere without colliding with anything on the way ..."
Sadly, CapsuleCastAll isn't up for the task. The main issue being that it seems as if under *some* circumstances when a collider is already intersecting the capsule, it won't register a hit when you call CapsuleCastAll. Now I know some people are going to say that this is how (ray-) casts are supposed to work, that when the ray originates from inside a collider that collider will not be hit. But that's not what's happening with CapsuleCastAll. If you look at this screenshot:
Each colored box in the console log represent one press of W to move the soldier forward so he intersects with the cube, and you will see that the three red ones only OverlapSphere detect the collision with the box, even though the character clearly is colliding with it. Now, I've tried to find the reason for the behavior that CapsuleCastAll exhibits here, but I've not managed to dig anything up, the assumption I have is that it somehow has something to do with how the physics engine updates (since the cast is done inside Update and not in FixedUpdate), but not sure.
Anyway, CapsuleCastAll gives unreliable results and the same goes for SphereCastAll. The only function which lets you check an area (and get the intersecting colliders back as a return value) with 100% certainty is OverlapSphere. So the way I've managed to get around this is to use several spheres that define the body of your character and then use OverlapSphere to check them all, and then on-top of that implement my own skin width / collision detection, this is how it looks in the editor:
Now this works, but it complicates the code a lot and is a decent amount slower (as you need to check 4 volumes instead of one, even though Sphere overlaps are very fast) then just using one collider. So, we've come to my final reason for writing this post, a couple of questions, both directed at Unity in general and at the community.
- Why is the API of the underlying PhysX engine not exposed properly? The reason I'm asking is because being able to have access to OverlapCapsule would be great. And considering we already have CheckCapsule, it can't be that big of a leap.
- Has anyone managed to implement a fully working character controller, that is what people would call "AAA"-grade? And if so, how was this done?
Thank you for your detailed and precise summary. I´m looking for alternatives for Unitys CC because of the limitations you mentioned. Especially handling dynamic environments (e.g. jumping onto moving platforms/physic driven cars but also simple stairs and slopes etc.) gives me some headache. Not to speak of dynamic animation adaption (I´m glancing at Unity4 here).
But anyway your post touches a much more basic problem and I would be happy if someone with experience in this area would speak up here.
xadhoom: I'm going to try to post an update response tomorrow with more info, but in the mean time you might wanna check out this asset I have for sale: http://u3d.as/content/fholm/rpg-controller-camera/2Jh yes it's a shameless self-plug, but it implements the technique I talk about in my original post.
It's the best way I've found at least, working on an update 1.4 version.
About the jittering with the rigidbody, setting the rigidbody to interpolate ought to take care of that. I just tested it to make sure by running my project at 60fps and the fixedTime at half that rate.
My character controllers are alle non-kinematic rigidbodies, got the tip from someone here in the forums. I constantly zero, and then set, the velocity of the rigidbody.
I scan the ground during movement to create my target movement vectors. Do the usual lerp, them feed them to the rigidbody. Controlling it like this seems to be pretty precise, and it's also easy to relinquish control at let it behave like a normal rigidbody again. The collision seems pretty stable, and they can still be pushed around by other rigidbodies.
When colliding with other rigidbody characters, then the weight still plays a factor in who's going to push who. And it might become less predictable, but it is still not bad be able to set priority by assigning a heavier weight.
The problem with setting the rigidbody to interpolate is that you end up with input lag that is on average equal to half your fixed update rate. But yes, interpolating rigidbodies help if that is acceptable, the problem is that for fast action paced multiplayer games this is not acceptable.
Hmm, but if your game runs at 60fps, and your fixed update is lower than that since it causes jittering. Say 30fps. Then wouldn't any lag still be less or equal to your framerate and not noticeable? I'm curious, since I use interpolate myself.
Have you tried multiple raycasts? As long as your casts are spaced closely enough that theres no blocking objects thinner than the width between them this approach should work.
I'd say around 24 ray casts should give pretty good collisions, you can even attach them to bones so they animate. If your AI's need complex collisions too you might run in to troubles (depending on how many AIs there are).
EDIT: You could combine this solution with triggers as well (so that you only fire the raycasts in the directions that have something nearby).
EDIT(2): Or use colliders, then fire ray casts in the direction of the intersection point to see what's in the way.
EDIT (3): Do you need this kind of accuracy for your solution? I'm coming at it from a platform point of view where near pixel accuracy is important.
Last edited by JohnnyA; 07-05-2012 at 06:56 AM.
I'd be interested to know what kind of results you get with this: http://docs.unity3d.com/Documentatio...epTestAll.html
JohnnyA: Raycasts will never be accurate enough, sadly. to cover the whole volume of a character of height 1.75 units you would need a grid about 20 rays (0.05 spacing) width and 35 rays high, which ends up being 700 rays. It's just not feasible.
I need absolute accuracy, no room for error.
flaminghairball: It has the same problem as CapsuleCastAll.
I have had issues with both ways..RB and CC. To speed dev time I had to revert to a CC but no matter what tricks I try about 60% of the time the game start with feet penetrating from ankle depth to knee depth at worst. It matters not how I set the skin width, set the CC right on the surface or above. As soon as I walk or run it conforms to the surface. I am doing quadrapeds so it is even more proper to use a RB setup than a CC which leaves the butt or head hanging out of you have to make it way too wide and tall to stuff all the parts inside. Sop an RB setup attached to the rig would be ideal for ragdolling and collision detection but moving it is awkward as all get out.
You mentioned you run the collision checks outside of FixedUpdate(), I'd assume that makes it prone to errors. For instance with a 0.033ms FixedUpdate and a 0.016 update, I guess you might get the same collisions as last frame, on every other update, in combination with moving platforms and the like. The character might be moved into an apparently open space in update, and find himself inside a moving collider on the next fixedupate.
It seems like with the kind of precision you seek, you should set the fixed update to run at least as fast, or faster than, your update. Then you could use rigidbodies that should move smoothly without interpolation, and you'd have PhysX's own collision precision.
Futurerobot: Yes, but if you think about it, it's not a problem. If stuff that is driven by the physics engine only moves in FixedUpdate, and fixed update runs at 30 fps, and my game at 60fps. I will get two frames with the same collision, but that's not a problem, since the stuff in FixedUpdate only *moves* at 30 fps also.
Plus, there are other huge problems with using rigidbodies as you can't reset position, or correct it (properly) based on server response, etc. (this is an FPS specific issue, as client prediction usually only is used fast paced FPS games).
I guess it might only be a problem with moving platforms, if they run in at a lower framerate than the game as a whole, then any character interacting with them would inherit the "jittering."
Maybe an odd avenue, but have you tested using a rigidbody as you collision "explorer"? Not as the mode of movement in itself, but by using for instance the RigidBody.SweepTestAll, and then handling the movement of your character yourself. It might give more accurate results, but I haven't tested it myself. At least it should support all collidertypes, and it seems even multiple colliders.
@fholm: No problem
I´m happy to pay for helpful scripts. Is there a webplayer available to see how the CC behaves under certain conditions?