Coroutine with while loop vs update with if condition - c#

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 :)

Related

unity make a delay between switching panels

I currently have such a piece of code in panelmanager where I open certain panels (I have a similar piece of code for closing panels). But I wanted to make sure that after pressing the button, there was no instant switching between panels, but with a delay. I read that it can be done with coroutines, but I haven't succeeded yet, since I'm probably using it incorrectly. Please tell me how I can implement such a delay correctly, I'm a complete noob..
public void OpenPanel(string name)
{
switch (name)
{
case nameof(MainMenu):
StartCoroutine(CoroutineSample());
MainMenuPanel.gameObject.SetActive(true);
break;
case nameof(LevelsPanel):
StartCoroutine(CoroutineSample());
LevelsPanel1.gameObject.SetActive(true);
break;
}
}
private void Start()
{
StartCoroutine(CoroutineSample());
}
private IEnumerator CoroutineSample()
{
yield return new WaitForSeconds(2);
}
You have to call the
MainMenuPanel.gameObject.SetActive(true);
inside the coroutine after the yield return line.
As it is in your code the StartCoroutine just starts the coroutine and then continues immediately with the next line. So it won't wait. And the WaitForSeconds call doesn't help anything because after the 2 seconds the coroutine doesn't do anything.

What is the difference between yielding a coroutine function call and yielding a startcoroutine call

Unity coroutines can be chained together sequentially using something like the following
private void IEnumerator Outer()
{
yield return StartCoroutine(Inner());
}
private void IEnumerator Inner() { yield break; }
However, the following also seems to do the same thing
private void IEnumerator Outer()
{
yield return Inner();
}
What actually is the difference between the two methods, is one preferred over the other.
I have only really seen the first in examples but the second has some really key benefits it seems (mainly not needing a monobehaviour to call StartCoroutine on)
Fast googling results in this thread with good explanations (IMO).

Set a class variable in IEnumerator coroutine

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.

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.

Is a while loop that only yields the best practice for pausing a function?

I have a function that calls a first dialogue box, then needs to wait for the user to press the space bar before it displays the second dialogue box. I can accomplish this easily with a coroutine, by yielding within a while loop as follows:
message.PlayMessage();
while (Input.GetKeyDown (KeyCode.Space) == false) {
yield return null;
}
message.PlayMessage(2);
My question is: is this a weird solution? I feel like there might be an actual function for this, and I fear that maybe this is eating up a lot of system resources for no reason.
It'll be called once in a frame, not expensive.
And making Wait function is more convenient.
IEnumerator MyMethod() {
message.PlayMessage();
yield return StartCoroutine(WaitForKeyDown(KeyCode.Space));
message.PlayMessage(2);
}
IEnumerator WaitForKeyDown(KeyCode keyCode)
{
while (!Input.GetKeyDown(keyCode))
yield return null;
}
And call.
StartCoroutine(MyMethod());

Categories