How to know if a coroutine is still running? - c#

I have a coroutine which takes in some variable running in the update function and I need the code to be something like this:
void Update(){
if(/*coroutine is not running*/){
StartCoroutine(coroutine(some variable));
}
}
Is there a way to know if the coroutine is still running before I run it with some other variable. I know that there is a way of doing it where I put that coroutine into another coroutine and use yield return coroutine(some variable) and that should work. But in my case the variable that the coroutine takes in depends on an event that my script is subscribed to, so the above implementation won't work. So is a way to know if my coroutine is still running or not?

bool isCouroutineRunning = false;
void Update()
{
if(isCouroutineRunning == false)
{
StopCoroutine("MyCourutine");
StartCoroutine("MyCourutine",variable);
}
}
IEnumerator MyCourutine()
{
isCouroutineRunning = true;
yield return new WaitForSeconds(1.0f);
isCouroutineRunning = false;
}

Related

How do I fix my WaitForSeconds code that involves BoxColliders?

Recently I have decided to start creating a game, and during this time I have run into many problems, most I have been able to fix myself but I'm having trouble with this. Basically, I want my code to make a Box Collider appear when clicked, and then disappear after a certain amount of time.
Here's my code:
void Start()
{
StartCoroutine(bruh())
}
void Update()
{
IEnumerator bruh()
{
if (Input.GetMouseButtonDown(0))
{
this.GetComponent<BoxCollider2D>().enabled = true;
yield return new WaitForSeconds(2);
}
else
{
this.GetComponent<BoxCollider2D>().enabled = false;
}
}
}
Your co-routine wasn't doing anything after it resumed from the yield return. I assume you want;
...
this.GetComponent<BoxCollider2D>().enabled = true;
yield return new WaitForSeconds(2);
this.GetComponent<BoxCollider2D>().enabled = false;
...
Possible new up the WaitForSeconds and then return that because it looks like you are just creating it not using it.

Unity 'StartCoroutine' does not run twice when IEnumerator is passed as a variable?

So I've ran into something weird, when using a co-routine in Unity to simulate a NPC (walks towards a target, idles for x seconds, walks to a target --repeat--).
I've found that starting the co-routine with a variable that holds the IEnumerator will not run twice, while starting the co routine with the method passed in directly runs as expected, repeatable.
Why does this work this way?
Whats happening 'under the hood'? I cant wrap my head around why this is, and it bugs me.
Below my IEnumerator method that simulates idle time.
private IEnumerator sitIdle()
{
var timeToWait = GetIdleTime();
_isIdle = true;
yield return new WaitForSeconds(timeToWait);
_isIdle = false;
}
If this gets called a second time per Scenario #1 (below), it runs as expected when called multiple times. It just repeats the process over and over.
If however, if it gets called per Scenario #2 (below) as a variable, it will kick off once, but refuse to enter a second time and just plain 'skip' it in code.
void LateUpdate()
{
_idleRoutine = sitIdle; //this is not actually in the late update, just moved here for reference.
if (_agent.hasPath)
{
if (isTouchingTarget())
{
StartCoroutine(sitIdle2()); //Scenario #1
StartCoroutine(_idleRoutine); //Scenario #2
_currentTarget = null;
_agent.ResetPath();
}
}
Tl;dr: StartCoroutine(variable to IEnumerator) is not repeatable, while StartCoroutine(IEnumerator()) works fine, why cant I pass the IEnumerator as a variable?
The return value of SitIdle() is an IEnumerator. IEnumerators do not repeat once they are completed and have no iterations remaining, which is what StartCoroutine tells Unity to do. Each time the coroutine is resumed at a yield is another iteration Unity tells the coroutine to perform.
If you really would like to store the coroutine as a variable, so you can choose between one or another, you could store the method you're interested in as a delegate, then call that to get your fresh IEnumerator:
IEnumerator sitIdle()
{
var timeToWait = GetIdleTime();
_isIdle = true;
yield return new WaitForSeconds(timeToWait);
_isIdle = false;
}
IEnumerator sitIdleAlternative()
{
var timeToWait = GetIdleTime() + 2f;
_isIdle = true;
yield return new WaitForSeconds(timeToWait);
_isIdle = false;
}
delegate IEnumerator IdleDelegate ();
IdleDelegate _idleRoutine;
void LateUpdate()
{
_idleRoutine = new IdleDelegate(sitIdleAlternative); //this is not actually in the late update, just moved here for reference.
_idleRoutine = new IdleDelegate(sitIdle);
if (_agent.hasPath)
{
if (isTouchingTarget())
{
StartCoroutine(sitIdle2()); //Scenario #1
StartCoroutine(_idleRoutine()); //Scenario #2
_currentTarget = null;
_agent.ResetPath();
}
}
}

How can I run StartCoroutine once?

The code in my script works fine. But sometimes it does not drop after I contact the rigid body collides. When it touches another corner, it restarts the StartCoroutine. I want to run it once to get ahead of it. How can I get it? (More descriptive: In my game, a ball is falling from above, and it stops for 3 seconds when hit by obstacles. I do not want it to stop in that obstacle again after it has hit once.
public void OnCollisionEnter2D(Collision2D col)
{
if (col.collider.CompareTag("Player"))
{
hitEffect.transform.position = col.contacts[0].point;
hitEffect.gameObject.SetActive(true);
GameManager.Instance.playerController.anim.Squeeze();
col.gameObject.GetComponent<Rigidbody2D>().simulated = false;
StartCoroutine (SetKinematic_Coroutine(col));
}
}
public IEnumerator SetKinematic_Coroutine(Collision2D col)
{
yield return new WaitForSeconds(1f);
col.gameObject.GetComponent<Rigidbody2D>().simulated = true;
}
Best way for preventing multi call of a coroutine is to use a reference:
private IEnumerator coroutine = null;
private void Method()
{
if(condition == true && this coroutine == null)
{
this.coroutine = MyCoroutine();
StartCoroutine(this.coroutine);
}
}
private IEnumerator MyCoroutine()
{
yield return null;
this.coroutine = null; // Set it back to null when leaving the coroutine.
}
When the condition is met and the coroutine is null (you are not running it already), it will assign to the reference and call the starting of the coroutine. While the coroutine is running, this.coroutine is not null and the double condition cannot be met anymore. When the coroutine is done, this.coroutine is set to null so next time the double condition is run, they will be both true.
You can also use a basic boolean for flag, but the usage of the IEnumerator reference can also be used to cancel.
You could turn the collider of the ball off whilst it is stopped so it can't receive any more hits during that time.
public IEnumerator SetKinematic_Coroutine(Collision2D col)
{
//turn the collider off
col.gameObject.GetComponent<Collider2D>().enabled = false;
yield return new WaitForSeconds(1f);
col.gameObject.GetComponent<Rigidbody2D>().simulated = true;
//turn the collider back on after we have waited
col.gameObject.GetComponent<Collider2D>().enabled = true;
}
Add a list of objects already hit by it and, for every hit, check if the object it is colliding with is in the list, if not add it and run the code.
private List<Collider2D> collided = new List<Collider2D>();
public void OnCollisionEnter2D(Collision2D col) {
if (col.collider.CompareTag("Player") && !collided.Contains(col.collider)) {
collided.Add(col.collider);
// ...
The Contains call might give you trouble if to many colliders are added to the list frequently, but otherwise this should do it.

How to stop co-routine?

When two co-routines are running, how do you stop the first co-routine?
GLOBALS.stableTime = 5;
IEnumerator StableWaittingTime ()
{
yield return new WaitForSeconds (1f);
if (GLOBALS.stableTime == 0) {
GameManager.instance.LevelFaildMethod ();
} else {
GameManager.instance.stableWaittingTime.text = GLOBALS.stableTime.ToString ();
GLOBALS.stableTime--;
StartCoroutine ("StableWaittingTime");
}
}
There are three ways to stop coroutines.
The first is to call StopAllCoroutines(), which will obviously stop all running coroutines.
The second is to call StopCoroutine(coroutine), where coroutine is a variable name given to your IEnumerator.
And the third is to do a yield break from within the coroutine.
Worth noting is that both StopAllCoroutines and StopCoroutine can only stop a coroutine when the coroutine reaches a yield return *.
So if you have two coroutines with the same name and you want to stop the one you are executing in you do yield break.
Interestingly, if you want to stop every other coroutine besides the one you are executing in, you call StopCoroutines() from within that coroutine.
#Imapler answer is almost all you need. I would just add that StopCoroutine method of MonoBehaviour is overloaded and has 3 types of parameters, so it is possible to stop many coroutines of same name.
For your need here, just use yield break; like this:
void Start ()
{
StartCoroutine (StableWaittingTime ());
}
IEnumerator StableWaittingTime ()
{
yield return new WaitForSeconds (1f);
if (false)
{
// do something
}
else
{
// do something
StartCoroutine (StableWaittingTime ());
yield break;
}
}

No effects with WaitForSeconds

I want my character to stop when I press a button on my gamepad or keyboard. The character must do a specific animation when I press the button and nothing else, so no movement at all, just the animation.
I'm trying to figure out how WaitForSeconds works, but when I try to use it, it doesn't work. Here the code of the function that calls WaitForSeconds
public IEnumerator Wait()
{
yield return new WaitForSeconds (6);
}
When the bool variable animationTest is true I want the program to wait for 6 seconds
if (animationTest)
{
UnityEngine.Debug.Log ("check1");
StartCoroutine (Wait ());
UnityEngine.Debug.Log ("check2");
animationTest = false;
}
but this doesn't work! check1 and check2 are printed at the same time. I'm missing something. This runs in FixedUpdate().
The Coroutine does not work like this. It starts a new (parallel) execution.
In order to achieve the wait you'd have to do it in the IEnumerator.
public IEnumerator SomethingElse() {
animationTest = false;
Debug.Log("check1");
yield return new WaidForSeconds(6f);
Debug.Log("check2");
yield return true;
}
void FixedUpdate() {
if (animationTest) {
StartCoroutine(SomethingElse());
}
}
Now when you set the animationTest at some point, you should see the two logs with a time gap of 6 seconds in between.

Categories

Resources