Set a class variable in IEnumerator coroutine - c#

I need to do some initialization work in Update().
This intialization work takes some time, and I can't proceed with the usual code in Update() until this initialization has finished.
Also, this initialization requires some WaitForSeconds() to work.
I have therefore tried the following:
private bool _bInitialized = false;
private bool _bStarted = false;
void Update()
{
if (!_bInitialized)
{
if (!_bStarted)
{
_bStarted = true;
StartCoroutine(pInitialize());
}
return;
}
(...) do stuff that can only be done after initialization has been completed
}
However, it seems that I can't change the variable _bInitialized within the IEnumerator.
_bInitialized never becomes true:
private IEnumerator pInitialize()
{
WiimoteManager.Cleanup(_wii);
yield return new WaitForSeconds(2);
_wii = WiimoteManager.Wiimotes[0];
yield return new WaitForSeconds(2);
_wii.SetupIRCamera(IRDataType.BASIC);
yield return new WaitForSeconds(2);
_bInitialized = true; //this doesn't seem to work
yield return 0;
}
Could anybody tell me how to do that correctly?
Thank you very much!

I think that StartCoroutine isn't enumerating all the values for whatever reason.
As the Enumerator lazily generates its values, and not all the values are being generated,
_bInitialized = true;
is never called.
You can confirm this by adding
var enumerator = pInitialize(); while ( enumerator.MoveNext() )
{
// do nothing - just force the enumerator to enumerate all its values
}
As suggested in one of the comments by Antoine Thiry,
What may happen here is that your code in the coroutine is silently throwing and catching an exception, maybe some of the code in WiimoteManager has something to do with it.

Related

Coroutine with while loop vs update with if condition

I noticed that every time I started a coroutine, it would create a tiny bit of garbage. I just wanted to know if looping the coroutine forever would be a valid way to avoid that.
Instead of this:
void Update()
{
if (condition)
{
StartCoroutine(Example());
}
}
IEnumerator Example()
{
if (coroutineRunning)
{
yield break;
}
coroutineRunning = true;
//Run code
yield return new WaitForSeconds(0.1f);
coroutineRunning = false;
yield return null;
}
I could use this:
void Start()
{
StartCoroutine(Example());
}
IEnumerator Example()
{
while (true)
{
while (!condition)
{
yield return null;
}
//Run code
yield return new WaitForSeconds(0.1f);
}
}
I've already tested this and there is no GC allocation with looping the coroutine forever. I'm wondering if there is any downside to looping a coroutine like this.
A coroutine, being a parallel process to the main thread, should not create problems if it is always running, on the contrary, it will remove some work from the main tread and lighten it.
So, yes, the second code is more efficient than the first.
Consider that as you surely know, it is very important to take care of the performance of the code, but if there are performance problems, other aspects should be seen first.
if you think my answer helped you, you can mark it as accepted and vote positively. I would very much appreciate it :)

while loop inside a coroutine in Unity

I have two scripts. The first script gets called in the beginning as follows:
Script1.cs
private Script2 script2;
void Start () {
script2= (Script2) GameObject.FindObjectOfType (typeof (Script2));
StartCoroutine("CallTrigger");
}
IEnumerator CallTrigger() {
while(script2.hasTriggered == true){
Debug.Log("Success");
script2.hasTriggered = false;
}
yield return 0;
}
And my script 2 is as follows:
public bool hasTriggered = false;
Since my 1st script gets called first, I want the CallTrigger() function to wait till the bool in script2 is set to true. Unfortunately while() is not the right way I suppose since it is not working for me. I know the best way is to use Update() but I am using multiple instances of this script from which only some get called in the beginning.
So how do I make my CallTrigger await till the hasTriggered in script2 is set to true?
I want the CallTrigger() function to wait till the bool in script2 is set to true
You can simply use WaitUntil
IEnumerator CallTrigger()
{
yield return new WaitUntil(() => script2.hasTriggered);
Debug.Log("Success");
}
which basically equals doing something like
IEnumerator CallTrigger()
{
// As long as the flag is NOT set
while(!script2.hasTriggered)
{
// wait a frame
yield return null;
}
Debug.Log("Success");
}
You could also btw directly make it
// Yes, if Start returns IEnumerator Unity automatcally runs it as a coroutine
IEnumerator Start()
{
// Rather use the generic versions
script2 = GameObject.FindObjectOfType<Script2>();
yield return new WaitUntil(()=> script2.hasTriggered);
Debug.Log("Success");
}

How to wait Coroutine callback is executed?

I want to wait StartCoroutine callback is executed.
Anyone knows how to do this?
public float getXXX() {
var result;
StartCoroutine(YYY((r) => result = r)); // how to wait this?
return result;
}
private IEnumerator YYY(System.Action<float> callback) {
LinkedList<float> list = new LinkedList<float>();
while(timeleft > 0) {
timeleft -= Time.deltaTime;
list.add(transform.position.magnitude);
yield return new WaitForSeconds (WAITSPAN);
}
callback(list.max());
yeild return true;
}
You can't and shouldn't try to wait or yield for a coroutine function to return from non coroutine function (getXXX function). It will block in that non coroutine function until this function returns preventing other Unity scripts to run.
To wait for a coroutine function(YYY) in the getXXX function, you must also make the function you are making the call and waiting from in a coroutine function. In this case this is theYYY function, so that should be a corutine function too then you can yield it:
public IEnumerator getXXX()
{
float result = 0f;
yield return StartCoroutine(YYY((r) => result = r)); // how to wait this?
//Use the result variable
Debug.Log(result);
}
OR
If you don't want to make the getXXX function a a coroutine function then don't try to wait there. You can still use the result from the YYY coroutine function but don't try to return the result. Just use it to do whatever you want to do in that function:
public void doSomethingXXX()
{
StartCoroutine(YYY((result) =>
{
//Do something with the result variable
Debug.Log(result);
}));
}
The idea of using coroutine is to be able to do something over multiple frames. The void function will just do that in one frame. You can't yield/wait in a void or non IEnumerator/coroutine functio.
You can only wait inside a coroutine. To do this, your getXXX() method should also be a coroutine. Something like this:
public float someOtherMethod()
{
float result;
StartCoroutine(getXXX(out result));
return result;
}
IEnumerator getXXX(out float result)
{
//more code here...
yield return StartCoroutine(YYY((r) => result = r));
//more code here...
}
IEnumerator YYY(System.Action<float> callback)
{
//your logic here...
}
I found something you may be able to call the class and use the methods inside this code and modify to use this is very similar to your code but less complex:
using UnityEngine;
using System.Collections;
public class WaitForSecondsExample : MonoBehaviour
{
void Start()
{
StartCoroutine(Example());
}
IEnumerator Example()
{
print(Time.time);
yield return new WaitForSeconds(5);
print(Time.time);
}
}
this code was taken from: https://docs.unity3d.com/ScriptReference/WaitForSeconds.html
from there examples
The 5 is the amount of time it will wait unless you want to do this dynamically based upon something. Depends on what you want to do. However notice how they are calling the method Example() inside the StartCoroutine(Example()) and which will go to the IEnumerator Example() and within there you have WaitForSeconds(5); this will make the StartCoroutine wait for 5 seconds. This can be hardcoded or made to wait dynamically by calling another method within that class from within IEnumerator Example() this is just one of many ways you can attack this. Again depends on what you want to do. Actually, you might even be better off making this into a method that passes a value in to the method each time something like
IEnumerator Example(float flSeconds)
{
print(Time.time);
yield return new WaitForSeconds(flSeconds);
print(Time.time);
}
this way you can pass what is in your
LinkedList list = new LinkedList();
Every time

Create infinite loop in Unity with delay

I need to create a infinite loop in Unity without using the main thread? I saw some example, but it's not usefull:
while(true){
var aa;
debug.log("print.");
}
I want to add some delay like e.g. 2 seconds. If anybody knows the solution please help.
First define a Coroutines:
private IEnumerator InfiniteLoop()
{
WaitForSeconds waitTime = new WaitForSeconds(2);
while (true)
{
//var aa;
Debug.Log("print.");
yield return waitTime;
}
}
Then call it like that:
StartCoroutine(InfiniteLoop());
Added note:
If you happen to change Time.timeScale and don't want that to affect delay time, use:
yield return new WaitForSecondsRealtime(2);
Use this to create the loop;
private IEnumerator LoopFunction(float waitTime)
{
while (true)
{
Debug.Log("print.");
yield return new WaitForSeconds(waitTime);
//Second Log show passed waitTime (waitTime is float type value )
Debug.Log("print1.");
}
}
For calling the function, don't use Update() or FixedUpdate(), use something like Start() so you don't create infinite instances of the loop;
void Start()
{
StartCoroutine(LoopFunction(1));
}
Use coroutines..
//Call in your Method
StartCoroutine(LateStart(2.0f));
Then write coroutine like..
private IEnumerator LateStart(float waitTime)
{
yield return new WaitForSeconds(waitTime);
//After waitTime, you can use InvokeRepeating() for infinite loop infinite loop or you use a while(true) loop here
InvokeRepeating("YourRepeatingMethod", 0.0f, 1.0f);
}
Here is the documentation for InvokeRepeating():
https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html

Why does this Coroutine only run once?

"Something" is only printed once...
IEnumerator printSomething;
void Start () {
printSomething = PrintSomething();
StartCoroutine (printSomething);
}
IEnumerator PrintSomething () {
print ("Something");
yield return null;
StartCoroutine (printSomething);
}
The misstake in your approach is that you save the enumerator. A enumerator is already "enumerating" therefore giving the enumerator to the StartCoroutine-method twice basically results in direct exit of the coroutine as the enumerator has been used before. Starting the coroutine again can be done by calling the function again.
StartCoroutine(PrintSomething());
But instead of starting the coroutine over and over again try to use a loop inside instead.
while (true)
{
print("something");
yield return null;
}
This is better as internal handling of the coroutine and its overhead is unknown.
Try co-routine's name instead of a pointer. Or co-routine itself.
IEnumerator PrintSomething ()
{
print ("Something");
yield return null;
StartCoroutine ("PrintSomething");
}
Or
IEnumerator PrintSomething ()
{
print ("Something");
yield return null;
StartCoroutine (this.PrintSomething());
}
I ran into this exact same issue, Felix K. is right in that it assumes the IEnumerator has already been run and just immediately returns. My solution was to pass the function itself so that we generate a new IEnumerator each time it's called. I hope this helps someone else!
public IEnumerator LoopAction(Func<IEnumerator> stateAction)
{
while(true)
{
yield return stateAction.Invoke();
}
}
public Coroutine PlayAction(Func<IEnumerator> stateAction, bool loop = false)
{
Coroutine action;
if(loop)
{
//If want to loop, pass function call
action = StartCoroutine(LoopAction(stateAction));
}
else
{
//if want to call normally, get IEnumerator from function
action = StartCoroutine(stateAction.Invoke());
}
return action;
}

Categories