Search Unity

Timer's measurement depends on whether I have the object selected in the editor or not!!

Discussion in 'Physics' started by Ibizanhound, Mar 30, 2020.

  1. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    Hello everyone!
    I just started out with coding in Unity as a hobby and I decided to start with simulating a simple gravity force.
    I wanted to see how accurate the simulation is, so I placed a cube at a height of 9.81m
    and used a stopwatch to measure the time it gets to reach the floor expecting the value to be around 1 sec (give or take a few milliseconds, I am fine with that)
    The thing is..
    The stopwatch gives me a value of 1.08sec if don't have the object selected in the editor prior to pressing the play button and 0.8 sec if I have it selected!


    I tried using a FixedUpdate() as well but the same thing occurs..
    Any ideas?

    Thank you!

    Here is my code:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Timers;
    5. using System;
    6. using System.Diagnostics;
    7.  
    8. public class Gravity : MonoBehaviour
    9. {
    10.     float Mass = 0.1f;
    11.     Vector3 GravityVector = new Vector3(0, 0, 0);
    12.     Vector3 Velocity = new Vector3(0, 0, 0);
    13.     private Stopwatch stopwatch;
    14.     public TimeSpan timeElapsed { get; private set; }
    15.  
    16.  
    17.     void Start()
    18.     {
    19.         stopwatch = new Stopwatch();
    20.         stopwatch.Start();
    21.         GravityVector = (transform.TransformDirection(Vector3.down)).normalized * (Mass * 9.81f);
    22.     }
    23.  
    24.     void Update()
    25.     {
    26.         if (transform.position.y > 0)
    27.         {
    28.             Velocity += GravityVector * Time.deltaTime;
    29.  
    30.             transform.position += Velocity;
    31.         }
    32.         else
    33.         {
    34.             stopwatch.Stop();
    35.             print(timeElapsed.ToString());
    36.         }
    37.  
    38.         timeElapsed = TimeSpan.FromMilliseconds(stopwatch.ElapsedMilliseconds);
    39.     }
    40. }
    41.  
     
    Last edited: Mar 30, 2020
  2. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    467
    Seems like there's small hickup when you start the game. I'd suggest to start the game and let the script wait for a second so that everything is running smoothly and then start the fall.

    What I find more worrying is that you expect the time to reach the floor to be 1 second. The distance travelled for a given acceleration after the time t is given by:
    Code (CSharp):
    1. distance = 0.5 * gravity * t^2
    I would expect the cube to be at y==0 after sqrt(2) seconds, so roughly 1,41 seconds.
     
  3. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    This is why I was carrying out this test to begin with. I am combining learning programming with learning physics at the same time. This was to test my assumption that in 1 second it would reach the ground. I see now where I was wrong.
    Thank you.
    But, the main issue is why the timer is affected depending on which object I have selected. The results are consistent too.
    In any case how could I pause the process at the begging of the program as you suggest?
    I only now of task.Delay, but can that be used to paused momentarily the Update() process?
     
  4. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    467
    Since you like to learn something anyway, my suggestion is to use Coroutines. Search for StartCoroutine and use
    Code (CSharp):
    1. yield return new WaitForSeconds(1)
    This should put you down on the right track. You can put all your logic within a Coroutine without the awkward if-statements.

    let me know if you need further help with that
     
  5. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    I managed to make the simulation wait for a second and also instead of accelerating I gave the cube a constant speed of 9.81m/s and indeed as it is located at a height of 9.81meters, it hits the ground in 1sec and there is only minor variation when I choose another object on the list.
    So this shows that Unity's Update() updates at the correct pace, which was my experiment.
    But...
    this is valid when the mass of the object is equal to 1(which I am not even sure if its supposed to mean 1 kilogram).
    And this doesn't feel right.
    Any less than that and is falls too slowly, and higher than that and it falls too fast.

    This is how I get the gravity vector:
    GravityVector = (transform.TransformDirection(Vector3.down)).normalized * (Mass * 9.81f);
    And to get the Velocity vector: Velocity = GravityVector * Time.deltaTime;

    What do you think?

    ***EDIT***
    Oh my...
    I just realized that I am using the weight of the of the object instead of the "earth's acceleration" which is always constant on the surface of the earth to push it down!!
    No wonder it was increasing as mass went up!
    In my defense... this is exactly the reason why I am combining programming with physics. I want to understand both in depth :)
     
    Last edited: Mar 30, 2020
  6. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    467
    Newton chillaxed underneath a tree.. you’ll figure it out with 21st century computer technology!

    anyhow, I think it’s a nice idea to combine that and it seems you already learn from your mistakes :) I guess you’re on a good path.

    feel free to ask if something else weirds you out (physics related, I mean)
     
    Ibizanhound likes this.
  7. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    Haha thanks! I wish we were learning physics like this back in school!
    My troubles are not over though...
    As you said for a height of 9.81m it should take 1.4seconds to cover that distance.
    But my cube keeps covering it in just 0.86 sec.
    Could it be that Update() is updating faster than I want it to?
    Here is my code once more:
    Code (CSharp):
    1.  
    2. public class Gravity : MonoBehaviour
    3. {
    4.     Vector3 GravityVector = new Vector3(0, -1, 0) * 9.81f;
    5.     Vector3 Velocity = new Vector3(0, 0, 0);
    6.     private Stopwatch stopwatch;
    7.     public TimeSpan timeElapsed { get; private set; }
    8.     bool Wait = true;
    9.  
    10.     void Start()
    11.     {
    12.         stopwatch = new Stopwatch();
    13.     }
    14.  
    15.     IEnumerator ExampleCoroutine()
    16.     {
    17.         yield return new WaitForSeconds(1);
    18.         Wait = false;
    19.         stopwatch.Start();
    20.     }
    21.  
    22.     void Update()
    23.     {
    24.         if (Wait == true)
    25.         {
    26.             StartCoroutine(ExampleCoroutine());
    27.         }
    28.         else
    29.         {
    30.             if (transform.position.y > 0.0)
    31.             {
    32.                 Velocity += GravityVector * Time.deltaTime;
    33.  
    34.                 transform.position += Velocity;
    35.             }
    36.             else
    37.             {
    38.                 stopwatch.Stop();
    39.                 print(timeElapsed.ToString());
    40.             }
    41.             timeElapsed = TimeSpan.FromMilliseconds(stopwatch.ElapsedMilliseconds);
    42.         }
    43.     }
    44. }
     
  8. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    I am busting my head to understand why the simulation feels wrong and I did an interesting experiment that only left me more confused!!
    I created a second cube and applied a rigidbody to it (the one on the right).
    2 conclusions.
    1. At 9.81m height it also covers the distance in 1 second but at least visually it looks physically correct!
    2. The other weird thing is that in order for my cube to fall at the same rate as the rigidbody I had to reduce the gravity acceleration factor by dividing with 7 other wise it moved too fast!
    I feel totally confused...
     
    Last edited: Mar 30, 2020
  9. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    467
    I took the liberty of modifying your script to my taste.. to what I think is somewhat cleaner using the Coroutine. But don't take my word for it, it's just one way. Also, you forgot to multiply by Time.deltaTime in the velocity integration.

    In this version, you can also place another cube with a rigidbody component next to it. Set it to "Kinematic" and assign it to the "rb" property of the first cube. It will set isKinematic to false at the same time as the first one starts to fall, so you can compare them visually.

    Here's my version:
    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Diagnostics;
    5. using UnityEngine;
    6.  
    7. public class Gravity : MonoBehaviour
    8. {
    9.     Vector3 GravityVector = new Vector3(0, -1, 0) * 9.81f;
    10.     Vector3 Velocity = new Vector3(0, 0, 0);
    11.     private Stopwatch stopwatch;
    12.     public TimeSpan timeElapsed { get; private set; }
    13.  
    14.     public Rigidbody rb;
    15.  
    16.     void Start()
    17.     {
    18.         stopwatch = new Stopwatch();
    19.         StartCoroutine(ExampleCoroutine());
    20.     }
    21.  
    22.     IEnumerator ExampleCoroutine()
    23.     {
    24.         // Wait until starting hickups end
    25.         yield return new WaitForSeconds(1);
    26.  
    27.         stopwatch.Start();
    28.  
    29.         // If there is a rigidbody set, let it start falling
    30.         // at the same time as this body.
    31.         if (rb != null)
    32.         {
    33.             rb.isKinematic = false;
    34.         }
    35.  
    36.         // do this every frame
    37.         while (transform.position.y > 0.0)
    38.         {
    39.             Velocity += GravityVector * Time.deltaTime;
    40.  
    41.             // Your mistake was not to multiply by delta
    42.             transform.position += Velocity * Time.deltaTime;
    43.  
    44.             // This makes the while loop take a break until the next frame
    45.             yield return null;
    46.         }
    47.  
    48.         // Do the time measurement
    49.         stopwatch.Stop();
    50.         timeElapsed = TimeSpan.FromMilliseconds(stopwatch.ElapsedMilliseconds);
    51.         print(timeElapsed.ToString());
    52.     }
    53. }
    54.  
    And that's the result: Pretty close to 1,4142...
    Bildschirmfoto 2020-03-31 um 07.35.15.png
     
    Last edited: Mar 31, 2020
  10. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    First of all, thank you so much for your code.
    Second.. remember when I said I duplicated my cube to add to it a rigid body property to check how it would behave?
    Well that was a good thought, only problem is..
    I forgot to remove my custom gravity script from it!
    Duh-doy! hahaha

    So..
    Thank you for showing me the proper structure of a Coroutine!
    But other than that, the only thing that was missing from my script (besides updating the position based on the deltaTime), was to make the cube kinematic, which I don't even know how it works internally and I was trying to avoid to be honest.
    Basically I was trying to replicate a rigidbody's behavior by using physics formulas and nothing else.
    I wonder what extra parameters the kinematic property adds to the object..
    The only thing I can think of is perhaps air resistance?
    In any case thank you so much for your time!

    ***EDIT*
    Oh sorry! You didnt make it Kinematic! Quite the opposite!
    You re saying that if the object is NOT a Rigidbody, then also make sure its not kinematic as well, right?
    In any case I played around with the code a bit more and the thing that made the difference was multiplying as you said the position update with deltaTime!
     
    Last edited: Mar 31, 2020
  11. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    You were right about the hickup at the beginning!
    I would have never guessed it!
    I removed this part:
    Code (CSharp):
    1. if (rb != null)
    2.         {
    3.             rb.isKinematic = false;
    4.         }
    as well, and it still works fine and I feel a bit better about the whole thing being simulated based on physics formulas and nothing pre-built from Unity.
    Thank you so much!
     
  12. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    467
    No worries, happy to help :)

    The Cube (as in the GameObject) itself is not kinematic as in: It doesn't have this property. The rigidbody component that is attached to said GameObject is kinematic (or not). I suggest for you to go through the roll a ball tutorial to get a feel for everything and to really understand every step that is taken there. Those tutorials are a bicycle for the brain.
     
    Ibizanhound likes this.
  13. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    Sounds great! I 'll take a look.
    Thank you very much!