- Home /
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");
}
}
Answer by eskivor · Aug 04, 2017 at 04:42 AM
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
Answer by thepopil · Aug 04, 2017 at 04:22 AM
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.
Answer by seanjust530 · Aug 04, 2017 at 05:29 AM
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");
}
}