Another long gap between updates I’m afraid! I thought I’d gotten through the last of the really tricky physics issues once I’d sorted the gears, but oh no… Over the past month I’ve been battling through a bunch of problems with wheel physics, but I finally have a solution that I think is satisfactory (albeit at some performance cost unfortunately, as I’ll explain later).
There are two things to deal with when two physical objects contact each other (in this particular case, a wheel, and the ground), these being collision and friction. In Unity, you’d normally just attach colliders to your objects, and leave it to PhysX to take care of it all, but this is not particularly well suited for wheels. First of all, there are no colliders available that properly match the shape of a wheel (i.e. a cylinder), and that also roll smoothly across the terrain, really the only options are a sphere or capsule collider. Secondly, you don’t have enough control over friction, which is particularly important for wheels / tires. Unity’s built in wheel collider is a specialised solution that includes a spring / damper model, and must be attached to a vehicle chassis, not directly to the wheel itself, so it is not suitable for my purposes.
I tried several approaches to deal with the wheel to ground collision and friction response, which I’ll briefly go through now.
Built in collider
Until now the wheels had been set up to use a capsule collider, but this doesn’t conform to the wheel shape, the capsule ends stick out of the wheel itself which means the wheel can’t tilt over properly while contacting the ground. I even tried using a mesh collider to approximate the cylindrical shape of the wheel, but because it is faceted it doesn’t roll smoothly across the ground.
Raycast to find ground contact
Given that using a built in collider wasn’t going to work, the first thing I had to do was find the wheel’s contact point with the ground for myself. To do this, the wheel fires raycasts towards the ground (perpendicular to its rotational axis) to find a contact point and normal.
Custom constraint solver
I implemented a constraint solver that resolves the collision by calculating an impulse along the contact normal to push the wheel away from the ground so that it doesn’t penetrate. It also finds an impulse (tangential to the ground contact) to resolve friction, using a simple Coulomb friction model. Essentially, exactly what PhysX does internally to resolve contacts, except now I’m able to use my own contact points. This took a while to code up as I had to brush up on my physics knowledge a fair bit, it’s very easy to screw things up and introduce energy into the system if you’re not careful! Once I got it sorted though, it worked great, the wheel rolled smoothly across the ground, was able to tilt over properly, etc.
However, there was a problem, with heavy constructions the wheel would sink into the ground and spring back out. This is actually a similar issue to the laggy springiness my gear constraint solver suffers from. I think it’s because my solver update runs independently of the PhysX internal solver updates, and so doesn’t get a chance to properly “compete” with the built in constraints (joints etc.)
So, rather than use my own constraint solver, I tried to recreate it using a configurable joint (as available within Unity), connected to the wheel’s rigidbody. Every update the joint gets repositioned above the ground contact point, and has a linear limit set up to prevent the wheel going below the ground, thus providing the collision response.
For friction, the configurable joint’s velocity drive is set up to force the ground relative velocity (tangential to the ground contact) to zero. This alone would result in essentially infinite static friction, so a maximum impulse is set on the drive. As per Coulomb, this impulse clamping needs to be based on the contact normal impulse. However, there’s no way to find out what impulse PhysX is applying to constrain the wheel to the linear limit, so what to do? In the end I had to estimate it by using the approximate mass over the wheel and the force against gravity along the contact normal. Kludgy, but it’ll have to do.
This solution worked nearly as well as the custom constraint solver, and without wheels sinking into the terrain, so was the one I settled on. The only downside of this method is that when you have a lot of wheels, the configurable joints seem to incur quite a noticeable performance cost.
Hopping wheels and sagging joints
The wheel collision and friction were not the only issues to deal with. A problem I found a while ago after adding the servo part and using it in the game to build cars with steering, was that during cornering, the lateral forces would cause the wheel (or rather the hinge joints connecting it) to flop over. It would then lose contact with the ground, snap back, gain contact again, and so on, causing the wheel to skitter and hop across the terrain. Also, if the construction was heavy, the joints attaching the wheels would sag under the weight.
I tried to think of ways of forcing the wheel to remain vertical to the rest of the construction, but because the player is free to build in whatever way they want, I have no way to know which direction a wheel should be constrained in. To be honest, the physics engine isn’t really designed for what I’m trying to do, normally you wouldn’t model vehicle physics with joints and rigidbodies for all the moving parts, instead you’d build a specialised vehicle physics engine. For my game I can’t do this though, as players construct the vehicles themselves in game. In the end the only fix I was able to come up with was to increase the physics solver iteration count, which has the effect of tightening up the hinge joints and lessening their sloppiness. This comes at the cost of a significant performance hit though unfortunately.
The build is now updated with the new wheel physics so you can try it out! I’ve also added a wheel skid sound effect, and fixed a bunch of bugs, including a pretty major screw up with the rigidbody mass properties update that I’ve now sorted.