Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Lerp issues - Never reaching target and jumping to second lerp too early

Discussion in 'Scripting' started by Studio_AKIBA, Aug 9, 2016.

  1. Studio_AKIBA

    Studio_AKIBA

    Joined:
    Mar 3, 2014
    Posts:
    1,427
    Adapting Robert Utter's "Smootherstep" algorithm from this blog post, I was able to create a much more satisfying and closer to "correct to application" lerp approximation for what I needed, but I am having some trouble.

    The following code is quite a mess (I'm not a programmer, I'm an artist that started out in programming (not main focus)), but getting to the point...

    What I am hoping to have happen:
    Once triggered, the door slowly eases from it's closed position (closedPosition) to it's open position (openPosition), once this has occured, Unity waits for 1 second until starting the rotation.
    Once this second is up, the door object slowly begins to move at the same speed and ease from it's closed rotation (closedRotation) to it's open rotation (openRotation).

    What is happening:
    Once triggered, the door slowly eases from it's closed position (closedPosition) to it's open position (openPosition), but jumps to halfway through it's rotation lerp before finishing the position lerping, so neither lerps actually complete their full cycle, the first gets cut off, and the second starts halfway through.

    I am also hoping to get the custom lerping into a function of its own, but that is something to worry about once I have this working.

    Ignore the fact that the coroutine is called DoOpen, it was originally for just opening, but I have adapted it (when it works of course) to act as both, just haven't renamed yet (given the time it took to write this, could have just renamed it :p).

    Anyway, any ideas on what I have done, and what I can do to get this to what I was hoping to do?

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BulkheadDoor : MonoBehaviour
    5. {
    6.  
    7.     private GameObject door;
    8.     public float speed = 1.0f;
    9.     private float posJourneyLength;
    10.     private Vector3 closedPos = new Vector3(-0.55f, 1.05f, 0.05f);
    11.     private Vector3 openPos = new Vector3(-0.55f, 1.05f, 0.12f);
    12.     private Vector3 closedRot = new Vector3(-90f, 0.0f, 0.0f);
    13.     private Vector3 openRot = new Vector3(-90f, -105f, -0.0f);
    14.     public bool opening;
    15.     private float startTime;
    16.     private float fracJourney;
    17.     private float distCovered;
    18.     public bool completedPos;
    19.     public bool completedRot;
    20.     public Vector3 angles;
    21.  
    22.     void Start()
    23.     {
    24.         door = this.transform.FindChild("Door").gameObject;
    25.         startTime = Time.time;
    26.         posJourneyLength = Vector3.Distance(closedPos, openPos);
    27.         OpenDoor();
    28.     }
    29.  
    30.     public void OpenDoor()
    31.     {
    32.         if (!opening)
    33.         {
    34.             StartCoroutine(DoOpen(closedPos, openPos, closedRot, openRot, speed));
    35.         }
    36.     }
    37.  
    38.     IEnumerator DoOpen(Vector3 startPosition, Vector3 endPosition, Vector3 startRotation, Vector3 endRotation, float lerpTime)
    39.     {
    40.         float startTime = Time.time;
    41.         float endTime = startTime + lerpTime;
    42.         opening = true;
    43.         fracJourney = 0;
    44.         bool wait = false;
    45.         do
    46.         {
    47.             float timeProgressedPosition = (Time.time - startTime) / lerpTime;
    48.             timeProgressedPosition = Mathf.Sin(timeProgressedPosition * Mathf.PI * 0.5f);
    49.             timeProgressedPosition = timeProgressedPosition * timeProgressedPosition * timeProgressedPosition * (timeProgressedPosition * (6f * timeProgressedPosition - 15f) + 10f);
    50.             door.transform.localPosition = Vector3.Lerp(startPosition, endPosition, timeProgressedPosition);
    51.  
    52.             if(Vector3.Distance(door.transform.localPosition, endPosition) < 0.02f)
    53.             {
    54.                 door.transform.localPosition = endPosition;
    55.             }
    56.  
    57.             if(door.transform.localPosition == endPosition)
    58.             {
    59.                 if(!wait)
    60.                 {
    61.                     //yield return new WaitForSeconds(1);
    62.                     wait = true;
    63.                 }
    64.                 else
    65.                 {
    66.                     float timeProgressedRotation = (Time.time - startTime) / lerpTime;
    67.                     timeProgressedRotation = Mathf.Sin(timeProgressedRotation * Mathf.PI * 0.5f);
    68.                     timeProgressedRotation = timeProgressedRotation * timeProgressedRotation * timeProgressedRotation * (timeProgressedRotation * (6f * timeProgressedRotation - 15f) + 10f);
    69.                     door.transform.localEulerAngles = Vector3.Lerp(startRotation, endRotation, timeProgressedRotation);
    70.                 }
    71.  
    72.                 if (Vector3.Distance(door.transform.localEulerAngles, endRotation) < 0.02f)
    73.                 {
    74.                     door.transform.localEulerAngles = endRotation;
    75.                 }
    76.  
    77.                 if (door.transform.localEulerAngles == endRotation)
    78.                 {
    79.  
    80.                 }
    81.             }
    82.  
    83.             yield return new WaitForFixedUpdate();
    84.         }
    85.         while (Time.time < endTime);
    86.         opening = false;
    87.     }
    88. }
     
  2. Studio_AKIBA

    Studio_AKIBA

    Joined:
    Mar 3, 2014
    Posts:
    1,427
  3. xexuxjy

    xexuxjy

    Joined:
    Feb 10, 2014
    Posts:
    18
    Bit hard to tell directly, but don't you want to reset your start time variable when you begin your close? Assuming that your lerp time is the time taken for the door to open (or close) , you'd want to set that to a new start time when it begins the closing loop. Oh and something else - once you start closing your test for door position being end position is going to break and you'll be back in the opening code path.

    A better test might be something like :

    Code (CSharp):
    1.  
    2. DoOpen
    3.  
    4. start = time;
    5. end = start+lerpTime;
    6. bool closing = false;
    7. bool complete = false;
    8.  
    9. while !complete
    10. {
    11.     calculate lerpVal between start and end;
    12.     if(closing)
    13.     {
    14.        lerpVal = 1.0f-lerpVal;
    15.     }
    16.  
    17.     apply lerp to local position;
    18.  
    19.     if(localPosition 'close to' endPosition)
    20.     {
    21.        start = time;
    22.        end = start+lerpTime;
    23.        closing = true;
    24.     }
    25.  
    26.     if(closing && localPosition 'close to' startPosition)
    27.     {
    28.        complete = true;
    29.     }
    30. }
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,353
    As @xexuxjy said, you're not resetting the startTime.

    That being said, this would be a lot easier to read if you just make two coroutines, one for moving and one for rotating. Then just start the rotation one at the end of the moving one.

    Or you could do this with a Unity-animation, which will probably look at lot better than anything you do in code.
     
  5. Studio_AKIBA

    Studio_AKIBA

    Joined:
    Mar 3, 2014
    Posts:
    1,427
    After making some changes to my method, I have become a lot closer, but am having a slight problem with the closing "animation".

    The issue with the close is that it doesn't move the position smoothly, the position doesn't move at all, and then jumps to it's end position before starting the rotation.

    I have stepped through all this a few times now but can't work out why.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BulkheadDoor : MonoBehaviour
    5. {
    6.  
    7.     private GameObject door;
    8.     public float speed = 1.0f;
    9.     private float posJourneyLength;
    10.     private Vector3 closedPos = new Vector3(-0.55f, 1.05f, 0.05f);
    11.     private Vector3 openPos = new Vector3(-0.55f, 1.05f, 0.12f);
    12.     private Vector3 closedRot = new Vector3(-90f, 0.0f, 0.0f);
    13.     private Vector3 openRot = new Vector3(-90f, -105f, -0.0f);
    14.     public bool opening;
    15.     public bool completedPos;
    16.     public bool completedRot;
    17.     public Vector3 angles;
    18.  
    19.     void Start()
    20.     {
    21.         door = this.transform.FindChild("Door").gameObject;
    22.         posJourneyLength = Vector3.Distance(closedPos, openPos);
    23.     }
    24.  
    25.     public void OpenDoor()
    26.     {
    27.         if (!opening)
    28.         {
    29.             StartCoroutine(DoMovement(closedPos, openPos, closedRot, openRot, speed, false));
    30.         }
    31.     }
    32.  
    33.     public void CloseDoor()
    34.     {
    35.         if (!opening)
    36.         {
    37.             StartCoroutine(DoMovement(openPos, closedPos, openRot, closedRot, speed, true));
    38.         }
    39.     }
    40.  
    41.     IEnumerator DoMovement(Vector3 startPosition, Vector3 endPosition, Vector3 startRotation, Vector3 endRotation, float moveTime, bool closeDoor)
    42.     {
    43.         opening = true;
    44.         if (!closeDoor)
    45.         {
    46.             float startTime = Time.time;
    47.             float endTime = startTime + moveTime;
    48.             do
    49.             {
    50.                 float timeProgressedPosition = (Time.time - startTime) / moveTime;
    51.                 timeProgressedPosition = Mathf.Sin(timeProgressedPosition * Mathf.PI * 0.5f);
    52.                 timeProgressedPosition = timeProgressedPosition * timeProgressedPosition * timeProgressedPosition * (timeProgressedPosition * (6f * timeProgressedPosition - 15f) + 10f);
    53.                 door.transform.localPosition = Vector3.Lerp(startPosition, endPosition, timeProgressedPosition);
    54.                 yield return new WaitForFixedUpdate();
    55.             }
    56.             while (Time.time < endTime);
    57.             StartCoroutine(DoRotation(startRotation, endRotation, speed));
    58.         }
    59.         else
    60.         {
    61.             float startTime = Time.time;
    62.             float endTime = startTime + moveTime;
    63.             do
    64.             {
    65.                 StartCoroutine(DoRotation(startRotation, endRotation, speed));
    66.                 yield return new WaitForFixedUpdate();
    67.             }
    68.             while (Time.time < endTime);
    69.             float timeProgressedPosition = (Time.time - startTime) / moveTime;
    70.             timeProgressedPosition = Mathf.Sin(timeProgressedPosition * Mathf.PI * 0.5f);
    71.             timeProgressedPosition = timeProgressedPosition * timeProgressedPosition * timeProgressedPosition * (timeProgressedPosition * (6f * timeProgressedPosition - 15f) + 10f);
    72.             door.transform.localPosition = Vector3.Lerp(startPosition, endPosition, timeProgressedPosition);
    73.         }
    74.     }
    75.  
    76.     IEnumerator DoRotation(Vector3 startRotation, Vector3 endRotation, float moveTime)
    77.     {
    78.         float startTime = Time.time;
    79.         float endTime = startTime + moveTime;
    80.         do
    81.         {
    82.             float timeProgressedPosition = (Time.time - startTime) / moveTime;
    83.             timeProgressedPosition = Mathf.Sin(timeProgressedPosition * Mathf.PI * 0.5f);
    84.             timeProgressedPosition = timeProgressedPosition * timeProgressedPosition * timeProgressedPosition * (timeProgressedPosition * (6f * timeProgressedPosition - 15f) + 10f);
    85.             door.transform.localEulerAngles = Vector3.Lerp(startRotation, endRotation, timeProgressedPosition);
    86.             yield return new WaitForFixedUpdate();
    87.         }
    88.         while (Time.time < endTime);
    89.         opening = false;
    90.     }
    91. }
     
  6. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    if closeDoor is true then you're kicking off a new DoRotation coroutine every time that loop runs (line 65). Assuming you want to wait for DoRotation to finish you'd write
    Code (csharp):
    1.  
    2. yield return StartCourtine(DoRotation(), .......);
    3.  
     
  7. Studio_AKIBA

    Studio_AKIBA

    Joined:
    Mar 3, 2014
    Posts:
    1,427
    Ah, a lot closer.

    It now finishes the rotation before starting the movement.

    But, the position movement still seems to jump, when the rotation has ended about half a second after the object jumps back to it's position instead of lerping there.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BulkheadDoor : MonoBehaviour
    5. {
    6.  
    7.     private GameObject door;
    8.     public float speed = 1.0f;
    9.     private float posJourneyLength;
    10.     private Vector3 closedPos = new Vector3(-0.55f, 1.05f, 0.05f);
    11.     private Vector3 openPos = new Vector3(-0.55f, 1.05f, 0.12f);
    12.     private Vector3 closedRot = new Vector3(-90f, 0.0f, 0.0f);
    13.     private Vector3 openRot = new Vector3(-90f, -105f, -0.0f);
    14.     public bool opening;
    15.     public bool completedPos;
    16.     public bool completedRot;
    17.     public Vector3 angles;
    18.  
    19.     void Start()
    20.     {
    21.         door = this.transform.FindChild("Door").gameObject;
    22.         posJourneyLength = Vector3.Distance(closedPos, openPos);
    23.     }
    24.  
    25.     public void OpenDoor()
    26.     {
    27.         if (!opening)
    28.         {
    29.             StartCoroutine(DoMovement(closedPos, openPos, closedRot, openRot, speed, false));
    30.         }
    31.     }
    32.  
    33.     public void CloseDoor()
    34.     {
    35.         if (!opening)
    36.         {
    37.             StartCoroutine(DoMovement(openPos, closedPos, openRot, closedRot, speed, true));
    38.         }
    39.     }
    40.  
    41.     IEnumerator DoMovement(Vector3 startPosition, Vector3 endPosition, Vector3 startRotation, Vector3 endRotation, float moveTime, bool closeDoor)
    42.     {
    43.         opening = true;
    44.         if (!closeDoor)
    45.         {
    46.             float startTime = Time.time;
    47.             float endTime = startTime + moveTime;
    48.             do
    49.             {
    50.                 float timeProgressedPosition = (Time.time - startTime) / moveTime;
    51.                 timeProgressedPosition = Mathf.Sin(timeProgressedPosition * Mathf.PI * 0.5f);
    52.                 timeProgressedPosition = timeProgressedPosition * timeProgressedPosition * timeProgressedPosition * (timeProgressedPosition * (6f * timeProgressedPosition - 15f) + 10f);
    53.                 door.transform.localPosition = Vector3.Lerp(startPosition, endPosition, timeProgressedPosition);
    54.                 yield return new WaitForFixedUpdate();
    55.             }
    56.             while (Time.time < endTime);
    57.             StartCoroutine(DoRotation(startRotation, endRotation, speed));
    58.         }
    59.         else
    60.         {
    61.             float startTime = Time.time;
    62.             float endTime = startTime + moveTime;
    63.             do
    64.             {
    65.                 yield return StartCoroutine(DoRotation(startRotation, endRotation, speed));
    66.             }
    67.             while (Time.time < endTime);
    68.             float timeProgressedPosition = (Time.time - startTime) / moveTime;
    69.             timeProgressedPosition = Mathf.Sin(timeProgressedPosition * Mathf.PI * 0.5f);
    70.             timeProgressedPosition = timeProgressedPosition * timeProgressedPosition * timeProgressedPosition * (timeProgressedPosition * (6f * timeProgressedPosition - 15f) + 10f);
    71.             door.transform.localPosition = Vector3.Lerp(startPosition, endPosition, timeProgressedPosition);
    72.         }
    73.     }
    74.  
    75.     IEnumerator DoRotation(Vector3 startRotation, Vector3 endRotation, float moveTime)
    76.     {
    77.         float startTime = Time.time;
    78.         float endTime = startTime + moveTime;
    79.         do
    80.         {
    81.             float timeProgressedPosition = (Time.time - startTime) / moveTime;
    82.             timeProgressedPosition = Mathf.Sin(timeProgressedPosition * Mathf.PI * 0.5f);
    83.             timeProgressedPosition = timeProgressedPosition * timeProgressedPosition * timeProgressedPosition * (timeProgressedPosition * (6f * timeProgressedPosition - 15f) + 10f);
    84.             door.transform.localEulerAngles = Vector3.Lerp(startRotation, endRotation, timeProgressedPosition);
    85.             yield return new WaitForFixedUpdate();
    86.         }
    87.         while (Time.time < endTime);
    88.         opening = false;
    89.     }
    90. }
     
  8. xexuxjy

    xexuxjy

    Joined:
    Feb 10, 2014
    Posts:
    18
    Maybe something like this to avoid repeating some of the code :

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5.  
    6. using UnityEngine;
    7. using System.Collections;
    8.  
    9. public class BulkheadDoor : MonoBehaviour
    10. {
    11.  
    12.     private GameObject door;
    13.     public float speed = 1.0f;
    14.     private float posJourneyLength;
    15.  
    16.     private Vector3 closedPos = new Vector3(-0.55f, 1.05f, 0.05f);
    17.     private Vector3 openPos = new Vector3(-0.55f, 1.05f, 0.12f);
    18.     private Vector3 closedRot = new Vector3(-90f, 0.0f, 0.0f);
    19.     private Vector3 openRot = new Vector3(-90f, -105f, -0.0f);
    20.  
    21.     public bool opening;
    22.     public bool closing;
    23.  
    24.     public float startTime;
    25.     public float moveTime;
    26.     void Start()
    27.     {
    28.         door = this.transform.FindChild("Door").gameObject;
    29.         posJourneyLength = Vector3.Distance(closedPos, openPos);
    30.     }
    31.  
    32.     public void OpenDoor(Vector3 startPosition, Vector3 endPosition, Vector3 startRotation, Vector3 endRotation, float moveTime)
    33.     {
    34.         if (!(opening || closing))
    35.         {
    36.             openPos = startPosition;
    37.             closedPos = endPosition;
    38.             openRot = startRotation;
    39.             closedRot = endRotation;
    40.  
    41.             opening = true;
    42.             startTime = Time.time;
    43.         }
    44.     }
    45.  
    46.     public void CloseDoor()
    47.     {
    48.         if (!closing)
    49.         {
    50.             opening = false;
    51.             closing = true;
    52.             startTime = Time.time;
    53.         }
    54.     }
    55.  
    56.     void Update()
    57.     {
    58.         if (opening || closing)
    59.         {
    60.             float elapsedTime = (Time.time - startTime);
    61.             float timeProgressed = elapsedTime / moveTime;
    62.             timeProgressed = Mathf.Sin(timeProgressed * Mathf.PI * 0.5f);
    63.             timeProgressed = timeProgressed * timeProgressed * timeProgressed * (timeProgressed * (6f * timeProgressed - 15f) + 10f);
    64.  
    65.             if (closing)
    66.             {
    67.                 timeProgressed = 1.0f - timeProgressed;
    68.             }
    69.  
    70.             door.transform.localPosition = Vector3.Lerp(openPos, closedPos, timeProgressed);
    71.             door.transform.localEulerAngles = Vector3.Lerp(openRot, closedRot, timeProgressed);
    72.  
    73.             if (elapsedTime >= moveTime)
    74.             {
    75.                 if (opening)
    76.                 {
    77.                     CloseDoor();
    78.                 }
    79.                 else
    80.                 {
    81.                     Reset();
    82.                 }
    83.             }
    84.  
    85.         }
    86.  
    87.     }
    88.  
    89.     void Reset()
    90.     {
    91.         opening = false;
    92.         closing = false;
    93.     }
    94.  
    95.  
    96.  
    97. }
     
    Last edited: Aug 9, 2016
  9. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    All your movement if closeDoor is true will execute in 1 frame (lines 68-71)