When we were working on the controls and feel of the speedboat in Super Springbreak Speedboat Hero SD one thing we identified was that it was rotating … wrong. It felt a bit stiff and unnatural, and not like a boat on water at all.
With a real speedboat, the center of mass isn’t in the center of the boat – it’s towards the rear. So naturally we tried setting the center of mass to the rear of the boat. This is trivially easy to do in Unity:
rigidbody.centerOfMass = Vector3 (0, -2, 0);
And we played through again – now when you whomped into the banks or the poor hapless spectators the boat moved like it should. But when being steered, it still felt like a model being manipulated by controls, not a boat. So we needed to rotate it around this center of mass when steering too.
The easiest way to achieve this would be to open up the modelling tool (Maya, 3ds Max, Blender) and change the pivot point, or origin of the model. But this only lets us change the pivot point early in the process – it’s handy to be able to change it on the fly. This lets us do things like changing the pivot point as a boost effect comes in. And it also lets us change the pivot point on the fly while playing the game, in order to get a very quick sense for the feel of the handling. Thank you once again, Unity’s in-game variable twiddling!
So we did it on the fly, by transforming our single torque out into a couple. This is done by splitting the torque, n, out into two forces (of n/2) at positions 1 unit away from the pivot point. These two will sum to zero force, but n torque. Here’s this in Unity code:
//we could rigidbody.AddRelativeTorque(torque)
//but this way lets us pick the pivot point.
Vector3 pivotPoint = Vector3 (0, -2, 0);
rigidbody.AddForceAtPosition(transform.TransformDirection(torque / 2) , transform.TransformPoint(pivotPoint + Vector3.forward));
rigidbody.AddForceAtPosition(transform.TransformDirection(-torque / 2 ) , transform.TransformPoint(pivotPoint - Vector3.forward));
Since Rigidbody.AddForceAtPosition takes it’s arguments in world space, we first have to convert everything into world space – that’s what Transform.TransformDirection is doing.
Note that we’re cheating a bit as we know what plane the torque will be in – we know that the turns will be around the up axis as we’re turning a speedboat going forward. If you wish to provide torque arbitrarily, you’ll have to convert those Vector3.forwards into something perpendicular to the direction of rotation. It’s also worth noting that we’re relying on these to be exactly 1 unit in magnitude, so normalize them!
It’s also worth pointing out that if you just want to do this for a normal rotation, and aren’t using rigidbodies, then your life is a lot easier, as you can just use Transform.RotateAround
transform.RotateAround (Vector3.zero, Vector3.up, 20 * Time.deltaTime);