[SOLVED] How Can I Predict The Angle That A Wheel Will Stop Spinning At?

In my 2d game, I have a wheel that spins using the rigidbody function _rb.AddTorque(_torque) that I call in FixedUpdate. Also in FixedUpdate, I cap the wheel’s angular velocity so that it doesn’t exceed a certain amount. When the wheel has finally reached its max angular velocity, I set the torque to zero so that it slowly comes to a stop

private void FixedUpdate()
{
    _rb.AddTorque(_torque);

    if (_rb.angularVelocity >= _maxAngularVelocity)
    {
        _rb.angularVelocity = _maxAngularVelocity;
        _torque = 0;
    }
}

The wheel spins exactly how I want it to, but is there any way I can predict the exact angle that the wheel will land on before it finishes spinning? I don’t know much about physics, but if anyone can give me the general formula, I can try to implement it in code myself.

4423495--403534--wheel.gif

I think most game actually generate a random angle and spin it over time via lerping to land at the wanted position.

Perhaps. But I’d still like to learn how to predict the angle, just as an excuse to learn some physics.

Apply the same angular drag as the phyics engine does (id look for it in Nvidia website, or who ever is incharge of Box2D or how tge 2D system called), see how many iterations it would take to get to basically zero angular velocity, multiply the number of iterations with the time delta for each tuck. (Never done it, just assuming it works like that)

But why would you wanna do it like that?
Use as much smoke & mirrors as you can if its indistinguishable to the user

I agree with the “smoke & mirrors” thing but as an exercise it’s pretty simple.

Given an angular velocity of n-degrees/sec you can obviously easily calculate the angle over time however drag (damping) changes this. You can find the damping code here (line 217).

w *= 1.0f / (1.0f + h * b->m_angularDamping);

h is the time-step so in a typical case this is the “Time.fixedDeltaTime” or whatever you are using for the simulation step. w is the angular velocity.

1 Like

Hi MelvMay,
When I input the algorithm that you posted into my FixedUpdate function, it does accurately calculate the angular velocity of the wheel as it’s being dampened, but I was wondering if it would be possible to calculate the angle that the wheel will land on as soon as torque is applied to it? So right when the wheel starts spinning, we instantly know what the angle will be. Could you provide me with a little more detail on I can go about this? Thanks in advance.

Not sure I follow. When you add torque, it doesn’t change the angle there and then. Torque is just an angular force and it’s summed up until the simulation is run i.e. if you add torque several times, it’ll just all be used when the simulation runs. That’s the “b->m_torque” on line 207, where the angular velocity is changed with the torque you apply on the body. The inertia for that formula can be found with Rigidbody2D.inertia (actually 1/inertia). The angle is integrated at line 299.

Note you can always read the rotational angle with Rigidbody2D.rotation.

Ok, so that makes sense. I do add torque to the wheel on every frame in FixedUpdate, and that torque accumulates and affects the angular velocity. So my question then is: If I already know how much torque will be applied at each frame, can I write a function in the Start method that will predict how long the wheel will spin for and what its resultant angle will be? So as soon as you run the game, a text will print to the console displaying those two pieces of information.

You can’t calculate for arbitrary torque numbers because it’s relative to the object mass, like velocity, it’s a force being applied, not the actual property of the object. (sorry if there are better terms for this, I didn’t study physics in english)

I believe what he wants is a function that looks like this

float GetFinalAngle(float angularVelocity, float angularDrag/*, etc.*/){
//do calculations
return angle;
}

where you input the angular velocity of the target with all the needed information and it returns you the angle that the object will come to rest at.
I also assume he wants the angle as the euler angle of what ever axis spin is in 2D (if Y is for 3D i assume Z is for 2D?)

I’d fill up more of the function but I think I’ve already contributed as much as I can to this.

That’s pretty much it; I just don’t know how to do the calculations.

Here is an example of one way to do it. This example is based upon the most accurate way of doing it which is performing time-steps and waiting until the body has gone to sleep. You can wait until the body angular velocity has gone below the angular sleep tolerance but it is allowed to do that for the sleep period meaning it moves slightly further depending on the time-to-sleep.

Just be careful not to try this method with no damping otherwise it’ll just stop. There’s a more complex way to do this that would avoid that situation or alternately you could just add in an interation limit that throws an error.

This example should provide you with enough accuracy though.

When I try to open that project, I get the following error
4436380--405757--error.jpg
If I click ‘Retry’, I get the same error, and if I click ‘Continue’, I get errors in the console


I’m using Unity 2018.3.9f1

Try this link, the same project created in 2018.3.12f1. Note that resetting the packages works too.

1 Like

Wow, thank you so much for taking the time to write that out for me. I really appreciate it. Just one thing though: I notice that you spin the wheel by setting it’s angular velocity in the Start function, whereas I do it by constantly adding torque in FixedUpdate, and once the velocity reaches a certain amount, I cap it at that amount and shut off torque.

private void FixedUpdate()
{
    _rb.AddTorque(_torque);
    if (_rb.angularVelocity >= _maxAngularVelocity)
    {
        _rb.angularVelocity = _maxAngularVelocity;
        _torque = 0;
    }
}

Is there any way to still predict the angle by spinning the wheel the way that I do it? I prefer adding torque because it looks a little more natural to just immediately setting the velocity to a certain amount. Thanks again for the code. I know you didn’t have to spend as much time as you did on it, so I really appreciate it.

I don’t know if melvs code works with that, but you can gradually assing an increasing velocity from 0 to max with lerp over x time.

Yes, but you confirmed you wanted a function like this which specified the velocity:

float GetFinalAngle(float angularVelocity, float angularDrag/*, etc.*/)

Either way, how long it takes to slow down is always based upon its velocity at that point, it has nothing whatsoever to do with forces being added. Just run the function after they’ve been added.

In your fixed-update, you add torque as a force which won’t get added to the angular velocity until the simulation runs. Just change that to be an impulse using ForceMode2D.Impulse which changes the angular velocity immediately (you’ll need to scale it by the “Time.fixedDeltaTime” to get the same force). TBH though, this is exactly the same as adding a value to “Rigidbody2D.angularVelocity” directly so you might as well do that. Then just run the function after you’ve done it.

Try downloading this one that accelerates the object, continuously predicting the rotation at rest. Same function though.

using UnityEngine;
using UnityEngine.Assertions;

[RequireComponent(typeof(Rigidbody2D))]
public class CalculateAngle : MonoBehaviour
{
    public float Torque = 500.0f;
    public float MaxAngularVelocity = 1000.0f;
    public float AngularDrag = 0.7f;

    private float PredictedRotation;
    private Rigidbody2D Body;

    void Start()
    {
        Assert.raiseExceptions = true;

        Body = GetComponent<Rigidbody2D>();
        Body.angularVelocity = 0f;
        Body.angularDrag = 0f;
    }

    private void FixedUpdate()
    {
        if (Torque == 0f)
            return;

        var timeStep = Time.fixedDeltaTime;

        var angularVelocity = Body.angularVelocity + Torque * timeStep;
        if (angularVelocity >= MaxAngularVelocity)
        {
            angularVelocity = MaxAngularVelocity;
            Body.angularDrag = AngularDrag;
            Torque = 0f;
        }

        Body.angularVelocity = angularVelocity;
        PredictedRotation = PredictRotation(Body.rotation, angularVelocity, AngularDrag, timeStep);
    }

    void OnGUI()
    {
        GUILayout.Label($"Predicted Rotation: {PredictedRotation} ({Mathf.Repeat(PredictedRotation, 360-Mathf.Epsilon)})");
        GUILayout.Label($"Current Rotation: {Body.rotation} ({Mathf.Repeat(Body.rotation, 360-Mathf.Epsilon)})");
        GUILayout.Label($"Current Velocity: {Body.angularVelocity}");
    }

    float PredictRotation(float rotation, float angularVelocity, float angularDrag, float timeStep)
    {
        // If drag is zero then this integration method becomes an infinite loop.
        Assert.IsTrue(angularDrag > 0f);

        var sleepTime = 0f;
        var sleepToleranceSqr = Physics2D.angularSleepTolerance * Physics2D.angularSleepTolerance;

        // Iterate until the body would be asleep.
        while(sleepTime < Physics2D.timeToSleep)
        {
            angularVelocity *= 1f / (1f + timeStep * angularDrag);
            rotation += timeStep * angularVelocity;

            if ((angularVelocity * angularVelocity) <= sleepToleranceSqr)
                sleepTime += timeStep;
        };

        return rotation;
    }
}
1 Like

Dude, thank you so much! I don’t know how to repay you! That’s exactly what I wanted. It works PERFECTLY. I’m going to study this code and try to learn as much as I can from it. Thank you again. You’re AWESOME.

1 Like

You’re most welcome, good luck with your project.

Before spinning the wheel it’s stationary so you already know the angle (Rigidbody2D.rotation) so perhaps I don’t follow the question.