I have this code for my linear acceleration and deceleration.
var currentSpeed = velocity.translation.z;
var decelerationDistance = ((currentSpeed * currentSpeed) / movementSettings.acceleration) * 0.5f;
if (distanceFromTarget > decelerationDistance)
{
velocity.translation = float3(0f, 0f, min(currentSpeed + (movementSettings.acceleration * deltaTime), movementSettings.maxSpeed));
}
else
{
velocity.translation = float3(0f, 0f, max(currentSpeed - (movementSettings.acceleration * deltaTime), 0f));
}
Assuming the object is already pointed at the target, this accelerates an object up to its maximum speed at its acceleration rate, and then slows it down by the acceleration rate so that it comes to a stop exactly on the target.
I'd like to have the same behaviour apply to rotation (which is a quaternion). i.e. Assuming the object is stationary, smoothly rotate to a target rotation, obeying acceleration and max rotation speed limits.
I have made a start of converting this code to work on quaternions:
private static float Magnitude(Unity.Mathematics.quaternion quat)
{
return CalculateQuaternionDifference(Unity.Mathematics.quaternion.identity, quat);
}
private static float CalculateQuaternionDifference(Unity.Mathematics.quaternion a, Unity.Mathematics.quaternion b)
{
float dotProduct = dot(a, b);
return dotProduct > 0.99999f ? 0f : acos(min(abs(dotProduct), 1f)) * 2f;
}
...
var currentRotSpeed = Magnitude(velocity.rotation);
var rotDecelerationDistance = ((currentRotSpeed * currentRotSpeed) / movementSettings.acceleration) * 0.5f;
if (distance > rotDecelerationDistance)
{
velocity.rotation = slerp(Unity.Mathematics.quaternion.identity, mul(rotationDiff, velocity.rotation), movementSettings.acceleration);
}
else
{
velocity.rotation = slerp(Unity.Mathematics.quaternion.identity, mul(inverse(rotationDiff), velocity.rotation), movementSettings.acceleration);
}
My question is:
This doesn't follow any max rotational speed limits or stop at 0. What is the equivalent of min/max()
for a quaternion?
Some of the more practical ways of clamping a quaternion can easily done once it is broken down to angle/axis form, then clamping those as desired:
float angle;
Vector3 axis;
velocity.rotation.ToAngleAxis(out angle, out axis);
So, if your rotational velocity is expressed as a quaternion, and you want to clamp its magnitude, you can clamp the angle , then convert back to a quaternion:
// clamp rotational angle to be no more than 90 degrees
float maxRotSpeed = 90f;
// angle should only ever be non-negative but clamping negative shows intent
float clampedAngle = Mathf.Clamp(angle, -maxRotSpeed, maxRotSpeed);
Quaternion clampedRot = Quaternion.AngleAxis(clampedAngle, axis);
The other side of clamping a rotation would be clamping the axis of rotation. To do that, you can clamp the axis then convert back:
// clamp rotational axis to aligned with mainAxis within a certain angle
Vector mainAxis = Vector3.up;
float maxDeviation = 10f;
Vector3 deviationDirection = Vector3.Cross(mainAxis, axis);
float deviation = Vector3.SignedAngle(mainAxis, axis, deviationDirection);
float clampedDeviation = Mathf.Clamp(deviation, -maxDeviation, maxDeviation);
Vector3 clampedAxis = Quaternion.AngleAxis(clampedDeviation, deviationDirection) * mainAxis;
Quaternion clampedRot = Quaternion.AngleAxis(angle, clampedAxis);