Vector2.Lerp() finishing instantly

I’ve been reading all other threads on this topic and still can’t seem to find what’s causing my lerp to finish immediately.

My game is a 2D platformer, and what I’m trying to accomplish is a dash attack that can be done at any angle (controlled by the joystick). There’s an invisible child object of the player that moves in a fixed position around the player using my MoveDashReticle() function. In Update(), the player presses the Dash button, upon which the Player and DashReticle’s positions are stored as variables and used in the lerp.

So far the dash attack works at all angles, so that is good. However, I don’t want the dash to happen instantly! As you can see, I’ve used a while-loop and time.deltaTime to gradually increase the perc float of the lerp, which to my understanding should do the trick. No luck though.

I apologize if my code is a mess, as I’m only a few weeks into learning. There is more to my script but it shouldn’t be necessary to include. Any help or suggestions are greatly appreciated, thank you!

public class Move2D : MonoBehaviour
{
    private GameObject player;
  
    //Movement and Sprite Flip
    Vector3 movement = Vector3.zero;
    public float moveSpeed = 10f;
    private bool facingLeft;
    private bool facingRight;
    private SpriteRenderer flip;
    private SpriteRenderer starFlip;
    private CapsuleCollider2D capCol;
    private Rigidbody2D rb;
    [SerializeField] private LayerMask groundLayerMask;
    [SerializeField] private LayerMask wallLayerMask;

    //Dashing
    [SerializeField] private Transform dashReticle;
    [Range(10.0f, 100.0f)] public float reticleDistance = 10.0f;
    private bool isDashing;
    [Range(0.0f, 1.0f)] public float dashTime = 1.0f;
    private float currentDashTime;
    Vector2 startPos;
    Vector2 dashRet;

    //Jumping
    public float jumpForce = 450f;
    public int extraJumps = 1;

    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
        flip = GetComponent<SpriteRenderer>();
        starFlip = GameObject.Find("Stars").GetComponent<SpriteRenderer>();
        rb = GetComponent<Rigidbody2D>();
        capCol = GetComponent<CapsuleCollider2D>();

    }

    void Update()
    {

        //Dashing
        MoveDashReticle();
      
        if (Input.GetButtonDown("Dash") && !isDashing)
        {
            //Get Player and Reticle Pos upon Input
            startPos = player.transform.position;
            dashRet = dashReticle.transform.position;
          
            isDashing = true;
            currentDashTime = 0.0f;

            while (currentDashTime < dashTime && isDashing)
            {
                float perc = currentDashTime / dashTime;
                currentDashTime += Time.deltaTime;
                transform.position = Vector2.Lerp(startPos, dashRet, perc);

                if (currentDashTime >= dashTime)
                {
                    currentDashTime = dashTime;
                    isDashing = false;
                    print(isDashing);
                }

            }
                     
        }
             

    }

    private void MoveDashReticle()
    {
        Vector2 aim = new Vector2(Input.GetAxis("AimX"), Input.GetAxis("AimY"));

        if (aim.magnitude > 0.0f)
        {
            aim.Normalize();
            aim *= reticleDistance;
            dashReticle.transform.localPosition = aim;
        }

    }

}

All of the code in your Update statement will execute all in a single frame. If you have a ‘while’ loop, it will loop over and over again, potentially locking up the game briefly, but all in a single frame. You seem to be expecting that each pass through the while loop will be performed on subsequent frames, thereby gradually increasing perc. But what’s really happening is that you’re looping through the while loop perhaps dozens of times in a single frame, and the value eventually ends up being “1” which is all that matters, so it gets to 1 in a single frame.

What you probably want is to either put your while loop in Coroutine, where you can yield every pass through the loop to break things up over many frames. Or you can remove the “while” loop entirely, and just keep what’s inside, in which case every Update that code will be called, getting a bit closer to the final value each frame. If you use the latter approach, just be aware that your GetButtonDown will only be true on a single frame, the one in which it’s pressed, so you would either want to use GetButton instead, or set a private variable indicating when Dash was pressed, and keep doing something for a while into the future afterwards.

1 Like

Thank you, that all makes perfect sense. I figured I may need to learn Coroutines in order to make this work, and it sounds like that’s the case. I’ll hash that out and come back here again if I still get stumped. Thanks!

I haven’t tested it, but there’s a reasonable chance that just moving the code inside of the condition into a Coroutine is all you need, remembering to add a ‘yield return null’ within the while loop. I haven’t tested this out, but your code might be find if you restructure it like this:

void Update()
{
    //Dashing
    MoveDashReticle();
   
    if (Input.GetButtonDown("Dash") && !isDashing)
    {
        StartCoroutine(PerformDash());                
    }    
}

private IEnumerator PerformDash() {
    //Get Player and Reticle Pos upon Input
    startPos = player.transform.position;
    dashRet = dashReticle.transform.position;
    isDashing = true;
    currentDashTime = 0.0f;

    while (currentDashTime < dashTime && isDashing)
    {
        float perc = currentDashTime / dashTime;
        currentDashTime += Time.deltaTime;
        transform.position = Vector2.Lerp(startPos, dashRet, perc);

        if (currentDashTime >= dashTime)
        {
            currentDashTime = dashTime;
            isDashing = false;
            print(isDashing);
        }
       
        yield return null;
    }
}

I haven’t tested it out, but that’s kind of the basic idea.

Hey that’s exactly what I did too, and it worked out perfectly! Thanks again!