Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

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

Discussion in 'Physics' started by sean244, Apr 12, 2019.

  1. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    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

    Code (CSharp):
    1. private void FixedUpdate()
    2. {
    3.     _rb.AddTorque(_torque);
    4.  
    5.     if (_rb.angularVelocity >= _maxAngularVelocity)
    6.     {
    7.         _rb.angularVelocity = _maxAngularVelocity;
    8.         _torque = 0;
    9.     }
    10. }
    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.

    wheel.gif
     
    Last edited: Apr 12, 2019
  2. SparrowsNest

    SparrowsNest

    Joined:
    Apr 6, 2017
    Posts:
    1,405
    I think most game actually generate a random angle and spin it over time via lerping to land at the wanted position.
     
  3. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    Perhaps. But I'd still like to learn how to predict the angle, just as an excuse to learn some physics.
     
  4. SparrowsNest

    SparrowsNest

    Joined:
    Apr 6, 2017
    Posts:
    1,405
    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
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,624
    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).

    Code (CSharp):
    1. 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.
     
  6. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    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.
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,624
    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.
     
  8. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    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.
     
  9. SparrowsNest

    SparrowsNest

    Joined:
    Apr 6, 2017
    Posts:
    1,405
    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
    Code (CSharp):
    1. float GetFinalAngle(float angularVelocity, float angularDrag/*, etc.*/){
    2. //do calculations
    3. return angle;
    4. }
    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.
     
  10. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    That's pretty much it; I just don't know how to do the calculations.
     
    Last edited: Apr 15, 2019
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,624
    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.
     
    Last edited: Apr 16, 2019
  12. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    When I try to open that project, I get the following error
    error.jpg
    If I click 'Retry', I get the same error, and if I click 'Continue', I get errors in the console
    console.jpg
    I'm using Unity 2018.3.9f1
     
    Last edited: Apr 16, 2019
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,624
    Try this link, the same project created in 2018.3.12f1. Note that resetting the packages works too.
     
  14. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    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.
    Code (CSharp):
    1. private void FixedUpdate()
    2. {
    3.     _rb.AddTorque(_torque);
    4.     if (_rb.angularVelocity >= _maxAngularVelocity)
    5.     {
    6.         _rb.angularVelocity = _maxAngularVelocity;
    7.         _torque = 0;
    8.     }
    9. }
    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.
     
  15. SparrowsNest

    SparrowsNest

    Joined:
    Apr 6, 2017
    Posts:
    1,405
    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.
     
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,624
    Yes, but you confirmed you wanted a function like this which specified the velocity:
    Code (CSharp):
    1. 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.
     
  17. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,624
    Try downloading this one that accelerates the object, continuously predicting the rotation at rest. Same function though.

    https://gyazo.com/45f3c451408661892f525959970d6632

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Assertions;
    3.  
    4. [RequireComponent(typeof(Rigidbody2D))]
    5. public class CalculateAngle : MonoBehaviour
    6. {
    7.     public float Torque = 500.0f;
    8.     public float MaxAngularVelocity = 1000.0f;
    9.     public float AngularDrag = 0.7f;
    10.  
    11.     private float PredictedRotation;
    12.     private Rigidbody2D Body;
    13.  
    14.     void Start()
    15.     {
    16.         Body = GetComponent<Rigidbody2D>();
    17.         Body.angularVelocity = 0f;
    18.         Body.angularDrag = 0f;
    19.     }
    20.  
    21.     private void FixedUpdate()
    22.     {
    23.         if (Torque == 0f)
    24.             return;
    25.  
    26.         var timeStep = Time.fixedDeltaTime;
    27.  
    28.         var angularVelocity = Body.angularVelocity + Torque * timeStep;
    29.         if (angularVelocity >= MaxAngularVelocity)
    30.         {
    31.             angularVelocity = MaxAngularVelocity;
    32.             Body.angularDrag = AngularDrag;
    33.             Torque = 0f;
    34.         }
    35.  
    36.         Body.angularVelocity = angularVelocity;
    37.         PredictedRotation = PredictRotation(Body.rotation, angularVelocity, AngularDrag, timeStep);
    38.     }
    39.  
    40.     void OnGUI()
    41.     {
    42.         GUILayout.Label($"Predicted Rotation: {PredictedRotation} ({Mathf.Repeat(PredictedRotation, 360-Mathf.Epsilon)})");
    43.         GUILayout.Label($"Current Rotation: {Body.rotation} ({Mathf.Repeat(Body.rotation, 360-Mathf.Epsilon)})");
    44.         GUILayout.Label($"Current Velocity: {Body.angularVelocity}");
    45.     }
    46.  
    47.     float PredictRotation(float rotation, float angularVelocity, float angularDrag, float timeStep)
    48.     {
    49.         // If drag is zero then this integration method becomes an infinite loop.
    50.         Assert.IsTrue(angularDrag > 0f);
    51.  
    52.         var sleepTime = 0f;
    53.         var sleepToleranceSqr = Physics2D.angularSleepTolerance * Physics2D.angularSleepTolerance;
    54.  
    55.         // Iterate until the body would be asleep.
    56.         while(sleepTime < Physics2D.timeToSleep)
    57.         {
    58.             angularVelocity *= 1f / (1f + timeStep * angularDrag);
    59.             rotation += timeStep * angularVelocity;
    60.  
    61.             if ((angularVelocity * angularVelocity) <= sleepToleranceSqr)
    62.                 sleepTime += timeStep;
    63.         };
    64.  
    65.         return rotation;
    66.     }
    67. }
    68.  
     
    SparrowsNest likes this.
  18. sean244

    sean244

    Joined:
    Nov 4, 2017
    Posts:
    26
    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.
     
    MelvMay likes this.
  19. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,624
    You're most welcome, good luck with your project.