Trouble understanding coroutines

I’m trying to make a flappy bird clone and I want it so that the player can’t jump repeatedly as much as he wants. So I researched coroutines and realized that they would work for preventing the player from spamming jump without a cooldown. I’ve watched videos and looked at the unity documentation but I still can’t understand what I’m doing wrong.
Here is what I have in my player controller script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    //Declaring Variables:
    public Rigidbody2D rb;
    public bool canJump;
    // Use this for initialization
    void Start()
    {
        canJump = true;
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        //Creates the force that pushes our player forward on the x axis
        rb.AddForce(new Vector2(600, 0) * Time.fixedDeltaTime, ForceMode2D.Force);

        //Jump
        if (Input.GetKey(KeyCode.Space) && canJump)
        {
            rb.AddForce(new Vector2(0, 2000) * Time.fixedDeltaTime, ForceMode2D.Force);
            canJump = false;
            StartCoroutine("JumpBreak");
            canJump = true;
        }
    }

    //Coroutine for preventing our player from spamming Jump
    IEnumerator JumpBreak()
    {
        Debug.Log("Start of coR");
        yield return new WaitForSeconds(2f);
        Debug.Log("End of coR");
    }
}

I think you’re pretty close. When you start a coroutine and call WaitForSeconds, it will just pause the function that it’s called in. So the rest of FixedUpdate() is still being called after we start the coroutine, which means we’re setting canJump immediately back to true.

Try moving the canJump assignments into the coroutine like this

IEnumerator JumpBreak()
{
    Debug.Log("Start of coR");

    canJump = false;

    yield return new WaitForSeconds(2f);

    canJump = true;

    Debug.Log("End of coR");
}

This way, we set canJump to false when we first call the coroutine. And it won’t get set back to true until we finish waiting.

Warning : If you get your inputs in a FixedUpdate () method, some of your inputs can be missed. Use an Update () method instead (I already had a bug like this in one of my previous projects).


Pro-tip : when you call your coroutine, avoid string as much as possible, StartCoroutine ("JumpBreak"); and StartCoroutine (JumpBreak ()); does the same thing, but with the second one, you’ll have auto-complete in your IDE (mono develop, visual studio, etc.) and with that auto-complete, you have no chance to make a misprint in the name of the coroutine you are calling.


When you call your coroutine, consider as starting a parallel process with its own timeline.
So when you call yield return new WaitForSeconds(2f); then only thing that will wait 2 seconds, are the lines under the yield in your coroutine, (here’s your Debug.Log("End of coR");), nothing else. All the lines that are called after the StartCoroutine ();, will be executed, as it does normally, just after the previous line, at the same frame. So, as it, your canJump will be false, then true (coroutine or not) at the same frame. So at the end of the frame, canJump will stay true;


To fix it : put the canJump = true; line inside of your coroutine, after the yield line

Thank you both @thepopil and @eskivor . I really appreciate the advice and I actually got it to work! The only issue is it only works if I take off Time.deltaTime from the parameters in AddForce. I wanted to keep deltaTime so that i could run independent of the frame rate. Even if I can’t fix that I’m grateful for the help.

Here is what I changed my script to:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    //Declaring Variables:
    public Rigidbody2D rb;
    public bool canJump;
    // Use this for initialization
    void Start()
    {
        canJump = true;
    }

    // Update is called once per frame
    void Update()
    {
        //Creates the force that pushes our player forward on the x axis
        rb.AddForce(new Vector2(600, 0) * Time.fixedDeltaTime, ForceMode2D.Force);

        //Jump
        if (Input.GetKey(KeyCode.Space) && canJump)
        {
            Debug.Log("Jump");
            rb.AddForce(new Vector2(0, 200), ForceMode2D.Force);
            StartCoroutine(JumpBreak());
        }
    }

    //Coroutine for preventing our player from spamming Jump
    IEnumerator JumpBreak()
    {
        Debug.Log("Start of coR");
        canJump = false;
        yield return new WaitForSeconds(0.5f);
        canJump = true;
        Debug.Log("End of coR");
    }
}