Search Unity

Lerping back and forth between two vectors?

Discussion in 'Scripting' started by BinaryOrange, Aug 28, 2014.

  1. BinaryOrange

    BinaryOrange

    Joined:
    Jul 27, 2012
    Posts:
    138
    Hi all,

    I'm trying to make a checkpoint object in my game lerp back and forth between two vectors, so that it appears to be floating up and down slowly in the gameworld. I attempted to use "Vector3.MoveTowards()", but that made it slide back and forth too fast and it looked very wrong.

    This is what I've come up with, however there is one issue:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class CheckPointFloating : MonoBehaviour
    5. {
    6.     public float speed;
    7.     public Vector3 startPosition;
    8.     public Vector3 endPosition;
    9.     private Vector3 target;
    10.  
    11.     void Start()
    12.     {
    13.         target = endPosition;
    14.     }
    15.     // Update is called once per frame
    16.     void Update ()
    17.     {
    18.    
    19.         // Move between the start and end vectors
    20.         if(transform.position == endPosition)
    21.         {
    22.             target = startPosition;
    23.         }else if (transform.position == startPosition)
    24.         {
    25.             target = endPosition;
    26.         }
    27.  
    28.         // Update position
    29.         transform.position = Vector3.Lerp(transform.position, target, speed * Time.deltaTime);
    30.  
    31.     }
    32. }
    The problem I'm having is that I'm checking to see if the current position of the object matches either the start or end vector, and when using the lerp function this produces the undesired result of the checkpoint object staying in one spot for about 5 seconds before it does equal either the start or end vector, and reverses the direction. I can't think of a way to check if the checkpoint object is at either vector that would allow me to use the Vector3.Lerp() function. Is there a way to say "if the checkpoint is at roughly this destination, reverse direction" in code that I'm just not seeing?

    Any help is appreciated! Thanks!
     
  2. smitchell

    smitchell

    Joined:
    Mar 12, 2012
    Posts:
    702
  3. smitchell

    smitchell

    Joined:
    Mar 12, 2012
    Posts:
    702
    I don't really like using it though, it doesn't always return true so I use my own version which has a tolerance value:

    Code (CSharp):
    1.        
    2.     bool isApproximate (float inputA, float inputB, float tollerance) {
    3.         return Mathf.Abs (inputA - inputB) < tollerance;
    4.     }
     
  4. smitchell

    smitchell

    Joined:
    Mar 12, 2012
    Posts:
    702
  5. BinaryOrange

    BinaryOrange

    Joined:
    Jul 27, 2012
    Posts:
    138
    Wow, thank you smitchell! That works quite well, and I definitely like the addition of the tolerance, makes things a little easier to customize!

    This is what my final script looks like:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class CheckPointFloating : MonoBehaviour
    5. {
    6.     public float speed;
    7.     public float tolerance;
    8.     public float startYPos;
    9.     public float endYPos;
    10.     private float target;
    11.     private Vector3 position;
    12.    
    13.     // Update is called once per frame
    14.     void Update ()
    15.     {
    16.         // Get current position
    17.         position = transform.position;
    18.         float curYPos = position.y;
    19.  
    20.         // Move between the start and end vectors
    21.         if(isApproximate(curYPos, endYPos, tolerance))
    22.         {
    23.             target = startYPos;
    24.         }else if (isApproximate(curYPos, startYPos, tolerance))
    25.         {
    26.             target = endYPos;
    27.         }
    28.  
    29.         // Update position
    30.         transform.position = Vector3.Lerp(position, new Vector3(position.x, target, position.z), speed * Time.deltaTime);
    31.  
    32.     }
    33.  
    34.     bool isApproximate(float a, float b, float tolerance)
    35.     {
    36.         return Mathf.Abs (a - b) < tolerance;
    37.     }
    38. }
    39.  
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    That can still fail if the framerate happens to hiccup at the wrong time, so I would suggest never relying on comparing values like that. Just lerp back and forth between the two positions; coroutines are ideal.

    Code (csharp):
    1. IEnumerator BackNForth () {
    2.     while (true) {
    3.         // lerp between point A and point B in a loop
    4.         // then lerp between point B and point A in a loop
    5.     }
    6. }
    --Eric
     
  7. BinaryOrange

    BinaryOrange

    Joined:
    Jul 27, 2012
    Posts:
    138
    I actually just thought of a great solution!

    Using something I borrowed from the physics tutorials, I made it so that the checkpoint stays within a trigger zone, where a constant (and limited) force pushes up on it, creating EXACTLY the effect I was going for!

    Here's the code...
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class HoveringObject : MonoBehaviour
    5. {
    6.     public float hoverForce;
    7.     public float maxForce;
    8.  
    9.     void OnTriggerStay(Collider other)
    10.     {
    11.         if(other.tag == "CheckPoint")
    12.         {
    13.             other.rigidbody.AddForce(Vector3.up * hoverForce, ForceMode.Acceleration);
    14.  
    15.             // Limit velocity of object
    16.             if(other.rigidbody.velocity.magnitude > maxForce)
    17.             {
    18.                 other.rigidbody.velocity = Vector3.ClampMagnitude(other.rigidbody.velocity, maxForce);
    19.             }
    20.         }
    21.     }
    22. }
    Simple, and within a FixedUpdate it works perfectly!