Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Having trouble refactoring an IEnumerator method with multiple yields

The gist of my code is as follows:

// Play the first beat
audio.PlayOneShot(beat);

// Show 1st heartbeat border flash
TweenAlpha.Begin(heartbeatPanel.gameObject, 0.1f, currentStress);
yield return new WaitForSeconds(0.1f);
TweenAlpha.Begin(heartbeatPanel.gameObject, 0.5f, 0);

yield return new WaitForSeconds(interval);

// Play the second beat
audio.PlayOneShot(beat);

// Show 2nd heartbeat border flash
TweenAlpha.Begin(heartbeatPanel.gameObject, 0.1f, currentStress);
yield return new WaitForSeconds(0.1f);
TweenAlpha.Begin(heartbeatPanel.gameObject, 0.5f, 0);

yield return new WaitForSeconds(interval * 2);

Now I want to split the above code into a single IEnumerator method with 2 calls.

This is what I came up with:

StartCoroutine(PlayBeat(currentStress, interval));
StartCoroutine(PlayBeat(currentStress, interval * 2));

// ...

IEnumerator PlayBeat(float currentStress, float interval)
{
     audio.PlayOneShot(beat);

     TweenAlpha.Begin(heartbeatPanel.gameObject, 0.1f, currentStress);
     yield return new WaitForSeconds(0.1f);
     TweenAlpha.Begin(heartbeatPanel.gameObject, 0.5f, 0);

     yield return new WaitForSeconds(interval);
}

The problem with this is that instead of the beats sounding with their correct interval, both beats sounded at the the same time and because I have these calls in an infinite loop, Unity crashed because the intervals are not being considered.

What's the best way to extract my two repetitive blocks of code above into a single IEnumerator method?

like image 488
Andreas Grech Avatar asked Mar 12 '13 08:03

Andreas Grech


1 Answers

Unity3d's Coroutines can be nested. To wait for a nested coroutine you need to yield it. So your function PlayBeat is fine; you just need to start it in a way that unity3d understands to mean "wait until complete". Your example would then look as follows:

yield return StartCoroutine(PlayBeat(currentStress, interval));
yield return StartCoroutine(PlayBeat(currentStress, interval * 2));

It's uncanny how similar that looks to C# 5's async/await. At some level this is unsurprising since they're both compiler-transformed "coroutine" statemachines - but it's still neat to see the similarity.

like image 156
Eamon Nerbonne Avatar answered Nov 13 '22 15:11

Eamon Nerbonne