Coroutine function stops at WaitForSeconds - c#

My coroutine funtion works only one time. The _isPaused variable is true. Can't find out what I did wrong.
IEnumerator Movement()
{
while (_isPaused) // I checked, it's true
{
Debug.Log("Some action");
yield return new WaitForSeconds(0.8f);
}
}
void Update ()
{
if (Input.GetKeyDown(KeyCode.P))
{
StartCoroutine(Movement());
}
}
Here is the complete code:

This is your code:
while (_isPaused)
{
Debug.Log("Some action");
yield return new WaitForSeconds(0.8f);
}
Your while loop will pause when it reaches the WaitForSeconds. It will pause because you're setting Time.timeScale to 0 somewhere in the code when "P" is pressed. This behavior is normal. It's made so that you can pause your coroutine functions with Time.timeScale. It will upause and continue to run as soon as you set Time.timeScale back to 1.
If you do not want Time.timeScale to pause your coroutine function when you're waiting with WaitForSeconds then use WaitForSecondsRealtime instead of WaitForSeconds.
while (_isPaused)
{
Debug.Log("Some action");
yield return new WaitForSecondsRealtime(0.8f);
}
This is because WaitForSeconds is implemented with Time.deltaTime or a similar property which also becomes 0 when Time.timeScale is set to 0 causing the timer to pause while WaitForSecondsRealtime is implemented with Time.realtimeSinceStartup which is not affected by Time.timeScale at all.

Without the whole code of the program, it is hard to tell what is wrong. I suggest you test the coroutine using true
IEnumerator Movement()
{
while (true)
{
Debug.Log("Some action");
yield return new WaitForSeconds(0.8f);
}
}
instead of using the _isPause field.

Related

Is there a reason why only one coroutine works in my code? [duplicate]

This question already has an answer here:
Wait for coroutine to finish [duplicate]
(1 answer)
Closed 3 years ago.
I have an enemy which currently attacks all the time, with no stopping in between attacks. I want to make enemy wait amount of time before he attacks again to simulate enemy "resting" and giving player a chance to attack him while not attacking. I needed coroutine to finish my animation playing so it can go to idle animation while waiting:
IEnumerator Attacking()
{
yield return new WaitForSeconds(animLenght);
isAttacking = false;
}
I have made another coroutine to wait for a second before enabling attacking again, but it doesen't work. Enemy attacks without brakes, like coroutine doesen't work:
IEnumerator WaitForAttack()
{
yield return new WaitForSeconds(1);
}
I have put WaitForAttack() coroutine in my Attack function:
private void Attack()
{
StartCoroutine(WaitForAttack());
isAttacking = true;
StartCoroutine(Attacking());
}
I would like to know what I'm doing wrong with coroutines, as I have just started using them, and this problem is troubling me for a very long time now.
The documentation for MonoBehaviour.StartCoroutine says
StartCoroutine function always returns immediately
So the Attack method will not wait after calling StartCoroutine(WaitForAttack()); and set isAttacking = true; immediately. Instead, set isAttacking in the coroutine itself. Also do both in the same coroutine to ensure the operations are performed in sequence. Otherwise both coroutines will run at the same time in parallel.
IEnumerator WaitAndAttack()
{
yield return new WaitForSeconds(1);
isAttacking = true;
yield return new WaitForSeconds(animLenght);
isAttacking = false;
}
private void Attack()
{
StartCoroutine(WaitAndAttack());
}
Coroutines can only suspend themselves (yield return ____) and not the method or object calling them. They are not the same as a synchronous method. When a coroutine is invoked and returns back to the parent method, that parent method will continue in the same frame.
In your method, you call "WaitForAttack()" and "Attacking()" from the same method on the same frame. "WaitForAttack()" literally does nothing.
Here's an example of a Coroutine that runs 5 separate times, once every second after it is called. Note that the var waitForSeconds is initialized once rather than every time I yield control back to the main thread. This is a minor optimization, but is considered best practice.
class TimedCoroutine : MonoBehaviour
{
var waitForSeconds = new WaitForSeconds(1);
IEnumerator CountdownToAction()
{
int countdown = 5;
while(countdown >= 0)
{
print(countdown--);
yield return waitForSeconds;
}
//Perform action here
}
}

Reset a repeating Coroutine delay

I'm using a Coroutine to set up a repeating delay as follows.
In my Awake I have
StartCoroutine(RandomMove());
And then further down
IEnumerator RandomMove()
{
while (true)
{
// print(Time.time);
yield return new WaitForSeconds(foo);
// print(Time.time);
}
}
where 'foo' is a random float value that I change with every iteration.
Lets say foo is 10 seconds and part way thru the delay I need to reset the delay so it starts 'counting down' from 10 again.
How would I accomplish this? Should I use a Timer instead?
I don't like either of the two existing answers. Here's what I'd do:
Kill and Restart the coroutine:
We'll start with this part of the killer_mech's answer:
Coroutine myCoroutine;
void Awake() {
myCoroutine = StartCoroutine(RandomMove());
}
But we're going to handle the rest differently. killer_mech never did anything with the reference, other than to keep ovewriting it.
Here's what we're doing instead:
public void resetRandomMove() {
StopCoroutine(myCoroutine);
myCoroutine = StartCoroutine(RandomMove());
}
Call this any time you need to reset it.
I would suggest you first store Coroutine in a variable.
Coroutine myCoroutine;
void Awake()
{
myCoroutine = StartCoroutine(RandomMove());
}
and change the coroutine function as
IEnumerator RandomMove()
{
// print(Time.time);
yield return new WaitForSeconds(foo);
// print(Time.time);
// Call your new coroutine here
myCoroutine = StartCoroutine(RandomMove());
}
this way you will have a coroutine variable for every iteration. If you need to stop the coroutine just say :
StopCoroutine(myCoroutine);
in your function at required time.This will allow you to stop a coroutine in middle before the end of countdown. Also at the end of coroutine it will start new coroutine with updated reference After finishing your task just call back again with
myCoroutine = StartCoroutine(RandomMove());
Hope this resolves your problem. Yes you can do it with timer also with a boolean flag the same thing but I think using coroutine is much simpler.
.
Hmmm it could something like this also . Just for my own .
void Start() {
StartCoroutine(RepeatingFunction());
}
IEnumerator RepeatingFunction () {
yield return new WaitForSeconds(repeatTime);
StartCoroutine( RepeatingFunction() );
}
As i understand the question. InvokeRepeating() is also a choice.
Maybe it is because you are each frame waiting for new assigned seconds?
Why don't you make the random before yielding the wait, and store the CustomYieldInstruction instead of yielding a new instance, since it disposes the one that was before, that creates memory problems. You won't notice that if you yield return a WaitForSeconds with a constant value, but maybe with a random one creates ambiguity and resets the timer (see this Unity's optimization page, on the Coroutine's section). A quick example:
public float foo;
public float min;
public float max;
void Awake()
{
StartCoroutine(Countdown());
}
IEnumerator Countdown()
{
while(true)
{
foo = Random.Range(min, max);
WaitForSeconds wait = new WaitForSeconds(foo);
yield return wait;
}
}
Also, #ryeMoss's solution seems a good one, to stop and restart the coroutine if 'foo' changes.
Hope it helps.

Code not starting/stopping animation

Logically this code looks fine to me but is not working as intended when i run the code the animation does not activate or deactivate. I put the qwerty int in there for testing purposes.
void WaitingForPipe () {
qwerty = 1;
PipeEntry.GetComponent <Animator>().enabled=true;
Wait ();
qwerty = 2;
//yield return new WaitForSeconds(2);
PipeEntry.GetComponent<Animator>().enabled=false;
qwerty = 3;
//GameObject.Find("FPSController").GetComponent("FirstPersonController").enabled=true;
}
IEnumerator Wait()
{
//This is a coroutine
//Debug.Log("Start Wait() function. The time is: "+Time.time);
//Debug.Log( "Float duration = "+duration);
yield return new WaitForSeconds(2); //Wait
//Debug.Log("End Wait() function and the time is: "+Time.time);
}
Coroutines in C# aren't methods that you run by just calling them, that only works in UnityScript. In order to start a Coroutine in C#, you must call StartCoroutine(YourCoroutine()); on a MonoBehaviour instance.
Your code would work if it looked like this:
IEnumerator WaitingForPipe()
{
PipeEntry.GetComponent <Animator>().enabled=true;
yield return new WaitForSeconds(2);
PipeEntry.GetComponent<Animator>().enabled=false;
}
and you have to start the Coroutine by using StartCoroutine(WaitingForPipe()); from somewhere.
You can read more about Coroutines here in the official documentation: https://docs.unity3d.com/Manual/Coroutines.html
It looks like you want to wait 2 seconds before running Wait ();. If that's true then you must yield your your Wait (); function.
Change
Wait ();
to
yield return Wait();
and you must change your WaitingForPipe function to a coroutine function too so that you can yield inside it. void WaitingForPipe () should be IEnumerator WaitingForPipe ()
Code not starting/stopping animation
There is no code in your question that is starting or stopping the animation, you do not and should not have to enable/disable the Animator in order to play or stop it. That's wrong. Remove your PipeEntry.GetComponent <Animator>().enabled=true/false code.
This the proper way to play/stop animation:
Play:
string stateName = "Run";
Animator anim = GetComponent<Animator>();
anim.Play(stateName);
Stop:
anim.StopPlayback();

Delay within an if in C#

How can I create a delay after the fade in, so that the text stays on screen for a few seconds? I used an IEnumerator and a coroutine, but it does nothing. I also tried placing it right after the first else.
What happens at the moment is the text fades out before having the chance to fade in. The text appears momentarily in semi-clear and disappears. It's for a Unity project.
Also, Thread.Sleep won't do.
Here's the piece of code in question:
IEnumerator Pause ()
{
yield return new WaitForSecondsRealtime(5);
}
void OnTriggerStay2D(Collider2D interCollider)
{
if (Input.GetKeyDown(KeyCode.E))
{
displayInfo = true;
}
else
{
displayInfo = false;
}
}
void FadeText()
{
if (displayInfo == true)
{
text1.text = string1;
text1.color = Color.Lerp(text1.color, Color.white, fadeTime * Time.deltaTime);
}
else
{
StartCoroutine(Pause());
text1.color = Color.Lerp(text1.color, Color.clear, fadeTime * Time.deltaTime);
}
}
Your code should read:
void Update()
{
if (fadingOut)
{
// fade out with lerp here
}
}
IEnumerator Pause()
{
yield return new WaitForSecondsRealtime(5);
fadingOut = true;
}
void FadeText()
{
if (displayInfo == true)
{
text1.text = string1;
text1.color = Color.Lerp(text1.color, Color.white, fadeTime * Time.deltaTime);
}
else
{
StartCoroutine(Pause());
}
}
You have the right idea of using a coroutine, but didn't quite get the execution right. When you invoke a method on coroutine, it will be executed in parallel with the main thread. In your code, the Pause() method is running alongside the Color.Lerp. If you want the fading to wait until after the pause is complete, they must be on the same coroutine.
Edit:
As pointed out, this won't work if you're calling FadeText() on each frame. But this shows you how you can easily set a flag and wait until the pause time is complete before fading.
You just need to add the text fade to the coroutine.
IEnumerator Pause()
{
yield return new WaitForSecondsRealtime(5);
text1.color = Color.Lerp(text1.color, Color.clear, fadeTime * Time.deltaTime);
}
And just start the coroutine in your else statement. This way it will execute the wait for seconds, and then the fade whenever you call it.
Most easy way is to use LeanTween asset. It's free and have a lot of other usefull features that I use in EVERY project.
It's really awesome lib.
LeanTween.DelayedCall(1f,()=>{ /*any code that will be called after 1 second will pass*/ });
or
LeanTween.DelayedCall(1f, SomeMethodWithoutParams());

C# : How couroutine stop the loop

I want to understand the concept of coroutine i don't know why code stop at when it print 1,2,3.But in this code loop should run 30 times and print value 1 to 30.
public class NewCore : MonoBehaviour
{
void Start ()
{
StartCoroutine (MyCoroutine (0.52f));
StartCoroutine (CoroutineKiller (2f));
}
IEnumerator MyCoroutine (float delay)
{
int value = 0;
while (value < 30)
{
yield return new WaitForSeconds (delay);//wait
value ++;
print (value);
}
StartCoroutine (MyCoroutine (delay));
}
IEnumerator CoroutineKiller (float delay)
{
yield return new WaitForSeconds (delay);
StopAllCoroutines ();
}
}
You are printing values from 1 - 30 with delay of 0.52sec, but after 2 seconds you stop doing so (you call StopAllCoroutines). Which is why you are only seeing 1, 2 and 3 printed out.
Try to comment out StartCoroutine (CoroutineKiller (2f));, or give it more delay to see how it controls the flow stop.
Coroutines are similar to threads - although not really - but yes in the sense that calling StartCoroutine does not block the code.
So when you're doing this:
void Start ()
{
StartCoroutine (MyCoroutine (0.52f));
StartCoroutine (CoroutineKiller (2f));
}
It will actually start both coroutines and execute them step by step, side by side, on each Update call.
If you wish to execute the coroutines one after another, try the following:
void Start()
{
StartCoroutine(F());
}
IEnumerator F()
{
// by yielding the Coroutine, it will block the execution of
// this coroutine (F) until MyCoroutine finishes
yield return StartCoroutine(MyCoroutine(1.52f));
// This is no longer doing much, really.
yield return StartCoroutine(Killer(2f));
print("done");
}
You can read more about the Execution Order which also includes information about coroutines and how the yield system actually works.
It seems you using StopCoroutine(MyCoroutine()); in CoroutineKiller and it stops coroutine after 2 seconds. (your parameter is 2f. And I think you used it for WaitForSeconds). So your MyCoroutine function working just 3 delay ( 0.52 + 0.52 + 0.52 =1.56) and before fourth delay ends, you stop this coroutine and you get just 1,2,3 printed.

Categories