I have a parent object which is a capsule collider with rigidbody. The game I'm making is 2D physics in an underwater environment, so the object has a configurable joint locking all axes except X and Y movement.
When the player presses the WASD keys or uses an XBox 360 controller to give movement input the appropriate force is applied to the capsule's rigidbody, pushing it through the water in that direction.
I need to be able to rotate a child of this object so that it looks like it is swimming in that direction. I can't match the parent's angle because it is locked, I need to match its direction of movement with Z rotation.
I just had to do a similar thing with a tank firing a projectile and I wanted the projectile to "Look" in its direction of movement. The code I used is the following called in Update() in a script attached to the projectile.
transform.rotation = Quaternion.LookRotation(rigidbody.velocity);
I had some problems at first with an odd twisting motion which was due to some non-uniform scaling on the object. If you have such scaling I would suggest parenting it to a non scaled empty gameobject.
An issue with setting the rotation directly on a rigidbody is that physics aren’t calculated for that movement. That’s why you have to apply torque to get hit registration, otherwise if you apply any rotation when colliding with something, you may ‘skip’ the collision.
I solved this by making a springy rotation lock to match the Spring Joint component but for rotation.
[RequireComponent(typeof(Rigidbody))]
public class SpringRotation : MonoBehaviour
{
public float force = 10f;
public Rigidbody target;
private new Rigidbody rigidbody;
private Vector3 torque;
private void Awake()
{
this.rigidbody = this.GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
if ((null == target) || !target) return;
// Determine Quaternion 'difference'
// The conversion to euler demands we check each axis
Vector3 torqueF = OrientTorque(Quaternion.FromToRotation(this.transform.forward, this.target.transform.forward).eulerAngles);
Vector3 torqueR = OrientTorque(Quaternion.FromToRotation(this.transform.right, this.target.transform.right).eulerAngles);
Vector3 torqueU = OrientTorque(Quaternion.FromToRotation(this.transform.up, this.target.transform.up).eulerAngles);
float magF = torqueF.magnitude;
float magR = torqueR.magnitude;
float magU = torqueU.magnitude;
// Here we pick the axis with the least amount of rotation to use as our torque.
this.torque = magF < magR ? (magF < magU ? torqueF : torqueU) : (magR < magU ? torqueR : torqueU);
this.rigidbody.AddTorque(this.torque * Time.fixedDeltaTime * force);
}
private Vector3 OrientTorque(Vector3 torque)
{
// Quaternion's Euler conversion results in (0-360)
// For torque, we need -180 to 180.
return new Vector3
(
torque.x > 180f ? 180f - torque.x : torque.x,
torque.y > 180f ? 180f - torque.y : torque.y,
torque.z > 180f ? 180f - torque.z : torque.z
);
}
}