Hello!
I’ve been working on a simple VR disc golf game for the Oculus platform. One of the aspects I’m trying to model is the turn and fade ratings of a disc.
All of the following commentary applies to a right-handed backhand throw…
The Turn rating of a disc dictates how far right a disc will turn while it is flying at higher velocity. The Fade rating dictates how far left a disc will turn as it slows down. During this entire time, the disc is spinning fairly rapidly. Below is the code which makes it happen, using AddTorque to both enforce this turn and correct wobble induced during the throw.
FixedUpdate
private void FixedUpdate()
{
if (!_isThrown) return;
// check if the disc has come to rest and skip flight calculations if so
if (Utility.FloatBetween(_rigidbody.velocity.sqrMagnitude, -.001f, .001f) &&
Utility.FloatBetween(_rigidbody.angularVelocity.sqrMagnitude, -.001f, .001f))
{
return;
}
// a disc will fly whether it is upside down or not
var discUpDir = transform.up;
if (discUpDir.y < 0) discUpDir = -discUpDir;
var velocity = _rigidbody.velocity;
var angularVelocity = _rigidbody.angularVelocity;
// because disc is spinning, we need to create our own transform.forward direction using velocity and the disc plane
var discForwardDir = Vector3.ProjectOnPlane(velocity.normalized, discUpDir).normalized;
var angleOfAttack = GetAngleOfAttack(velocity.normalized, discForwardDir);
var discArea = GetDiscArea();
var liftVector = GetLift(angleOfAttack, discArea, Rho, velocity, discUpDir, discForwardDir);
var dragVector = GetDrag(angleOfAttack, discArea, Rho, velocity);
var torqueVector = GetTorque(velocity, angularVelocity, discUpDir, discForwardDir);
_rigidbody.AddForce(liftVector);
_rigidbody.AddForce(dragVector);
_rigidbody.AddTorque(torqueVector);
}
GetTorque
private Vector3 GetTorque(Vector3 velocity, Vector3 angularVelocity, Vector3 discUpDir, Vector3 discForwardDir)
{
// the speed of maximum turn
var turnMax = RatedSpeed;
// the speed of maximum fade
var fadeMax = turnMax / 1.5f;
// the speed where the disc goes from turn to fade
var fadePoint = ((turnMax - fadeMax) / 2f) + fadeMax;
// converts speed to normalized between fade point and turn max or fade max
var speedToNorm = 1f / (turnMax - fadePoint);
var speed = velocity.magnitude;
// value between 0 and 1 representing the amount of fade or turn to apply
var turnAmnt = Mathf.Clamp(Mathf.Pow((speed - fadePoint) * speedToNorm , 2f), 0f, 1f);
if (speed > fadePoint)
{
turnAmnt *= -TurnRating;
}
else
{
turnAmnt *= -FadeRating;
}
var discRightDir = Vector3.Cross(discUpDir, discForwardDir).normalized;
var turnTorque = .0008f * turnAmnt * discRightDir;
var wobbleCorrectionTorque = GetWobbleCorrectionTorque(angularVelocity);
return wobbleCorrectionTorque + turnTorque;
}
private Vector3 GetWobbleCorrectionTorque(Vector3 angularVelocity)
{
var localAngularVelocity = transform.InverseTransformVector(angularVelocity);
var spinRate = localAngularVelocity.y;
if (Math.Abs(spinRate) < 10f)
{
return Vector3.zero;
}
var dampenAmount = .001f * Mathf.Clamp(Mathf.Pow(Mathf.Abs(spinRate), 2f) / 400f, 0, 1);
var localTorque = Vector3.zero;
localTorque.x = -localAngularVelocity.x * dampenAmount;
localTorque.z = -localAngularVelocity.z * dampenAmount;
return transform.TransformVector(localTorque);
}
Cool - and this works great on the Oculus Rift. You’ll notice that the torque for turning/fading is being applied around the right axis of the forward direction. I’m not perfectly sure why this works (I guessed because the disc is spinning and so by the time the front-side dips down, it has rotated to the side).
So here are the results on the Oculus Rift:
Works great. This disc begins fading left as it loses velocity. Then I went ahead and loaded it up on the quest using the same disc:
It is a bit hard to see, but the disc now fades right instead of left! I even throw a forehand at the end (which reverses the turn and fade directions) and its also opposite.
So I figured that maybe the Oculus Quest is just not able to keep up with the physics computation that I’m putting every fixed update. Initially I was using 1/90 for my fixed timestep. So I went ahead and lowered it to 1/45 and still got the same behavior (correct on the Rift and incorrect on the Quest). The footage above is shot at 1/72.
So I’m looking for some help and ideas on what could be happening. Even if the Quest is slowing down the physics step, shouldn’t that be accounted for when using Physics functions like AddTorque? I keep thinking that maybe the issue is that the Torque is being applied too late in the rotation on the Quest, making it apply to the opposite side that it should, but again, wouldn’t the angular velocity that’s spinning the disc also slow down in the same amount if that were true? Wouldn’t I be able to see this on the Rift when I change the fixed timestep to something like 1/45?
Any advice & thoughts are appreciated!