Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Buggy SmoothDamp!

Discussion in 'Scripting' started by rameshmadara, Sep 16, 2021.

  1. rameshmadara

    rameshmadara

    Joined:
    Nov 5, 2020
    Posts:
    48
    In this game Level, I want to lift a structure (a fence) off the ground piece by piece. I am using SmoothDamp in a Coroutine for this. But when the Coroutine is triggered the objects sometimes overshoot the target and certain objects are show a lot of jitteriness until the coroutine is stopped. What could be the problem?

    I tried this without TransformPoint(), didn't fix it.

    Code:
    Code (CSharp):
    1.  
    2.   [SerializeField] private GameObject[] fences;
    3.     [SerializeField] private Vector3[] fences2ndPos;
    4.     public bool openFence = false;
    5.     [SerializeField] private float newPosY, waitForSeconds, smoothing;
    6.  
    7.  void Start()
    8.     {
    9.         fences2ndPos = new Vector3[fences.Length];
    10.         for (int i = 0; i < fences.Length; i++)
    11.         {
    12.             Vector3 fencePos = fences[i].transform.TransformPoint(0, 0, 0);
    13.             fences2ndPos[i] = new Vector3(fencePos.x, fencePos.y + newPosY, fencePos.z);
    14.         }
    15.     }
    16.  
    17.  void Update()
    18.     {
    19.         if (openFence)
    20.         {
    21.             StartCoroutine(FenceOpen());
    22.         }
    23.         else
    24.         {
    25.             StopAllCoroutines();
    26.         }
    27.     }
    28.  
    29.     private void OnTriggerEnter(Collider other)
    30.     {
    31.         if (other.CompareTag("Player"))
    32.         {
    33.             openFence = true;
    34.         }
    35.     }
    36.     IEnumerator FenceOpen()
    37.     {
    38.         Debug.Log("up");
    39.         for (int i = 0; i < fences.Length; i++)
    40.         {
    41.             yield return new WaitForSeconds(waitForSeconds);
    42.             Vector3 itemPos = fences[i].transform.position;          
    43.          fences[i].transform.position = Vector3.SmoothDamp(itemPos, fences2ndPos[i], ref refPos, smoothing * Time.deltaTime);
    44.  
    45.             if (i == (fences.Length - 1))
    46.             {
    47.                 yield return new WaitForSeconds(0.1f);
    48.                 openFence = false;
    49.                 //stops the Coroutine
    50.             }
    51.         }
    52.         yield return null;
    53.     }
    54.  
     
    Last edited: Sep 16, 2021
  2. Cameron_SM

    Cameron_SM

    Joined:
    Jun 1, 2009
    Posts:
    915
    You're starting a new coroutine every frame in update once openFence is set to true. I suspect this was not your intention.

    Why not just star the coroutine in OnTriggerEnter instead of using the flag and the update loop?

    I also suspect you don't fully understand how Coroutines work. Once started, they will run until the method exits or the MonoBehaviour where it was started is disabled, deactivated or destroyed. You can suspend the method executing at any point with a yield instruction. The method will resume executing from the exact point it suspended (literally the next line) at some later point in time based on the yield instruction. That could be the next frame for a yield null or when a particular condition is met such as x time has passed with WaitForSeconds.

    Code (CSharp):
    1. public float duration = 2.0f;
    2.  
    3. IEnumerator MyCoroutine()
    4. {
    5.     var elapsed = 0.0f;
    6.     while(elapsed < duration)
    7.     {
    8.         // execute this code every frame until elapsed time passes
    9.         elapsed += Time.deltaTime;
    10.         yield return null; // half execution untill the next frame.
    11.     }
    12. }
    Note that this is not normally how IEnumerators are meant to be used. They're generally used for looping over a set of values (enumerating a set). Unity and the StarCoroutine method are kind of flipping that idea on its head and saying, well if a Method that returns an IEnumerator provides a set of items to iterate/loop over one item at a time, and it suspends executing that method for every yield instruction, we can use that as a nice way to sequence things in order and each yield could be instructions about what conditions must be met before asking for the next one (which in turn starts executing the code in the method from where it left off).

    The yield instructions inside an IEnumerator method are returning those instructions as an object that the StartCoroutine method is looping/iterating over in sequence. Once the method gets to the end and doesn't yield any more instructions the "Coroutine" ends.

    It's tricky to wrap your head around with just an explication so here's an example you can mess around with that uses a method as a kind of filter to only yield numbers greater than a set threshold.

    https://dotnetfiddle.net/Widget/96EFQ1

    You can see in the example that code execution jumps back and forth between the IEnumerator method and the code consuming the IEnumerator object whenever MoveNext() is called.
     
    Last edited: Sep 17, 2021
    rameshmadara likes this.
  3. rameshmadara

    rameshmadara

    Joined:
    Nov 5, 2020
    Posts:
    48

    That is really valuable insight. That example is very efficient in conveying the idea. Thanks a lot!
     
    Cameron_SM likes this.