Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
37
Question by KvanteTore · Mar 30, 2010 at 08:10 AM · coroutine

Nested coroutines

I have one routine, Func1 which returns an IEnumerator. This method is called by another coroutine Func2 that has been started with StartCoroutine. The intent is that Func2 should wait for the completion of Func1.

Ideally, I'd just yield the IEnumerator returned by Func1 and let the

IEnumerator Func1() { for (int i=0; i<10; i++) { yield return null; } yield break; }

IEnumerator Func2() { //wait for the completion of Func1 yield return Func1(); }

However, Unity doesn't seem to recursively iterate over IEnumerators, so Func2 yields just once. An alternative is to manually iterate over the IEnumerator,

IEnumerator Func2()
{
    //wait for the completion of Func1
    IEnumerator e = Func1();
    while (e.MoveNext()) yield return e.Current;
}

but I was looking for a less clunky solution, much like the way I can yield a WWW object and wait for its completion?

EDIT: Removed superfluous yield statement in func2. It turns out, that MoveNext should be called before accessing IEnumerator.Current

Comment
Add comment · Show 8
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image KvanteTore · Apr 12, 2010 at 12:58 PM 0
Share

I made a suggestion to help implement nested coroutines in Unity: http://feedback.unity3d.com/forums/15792-unity/suggestions/636531-make-yieldinstruction-extendable

avatar image cregox · Jan 04, 2013 at 03:53 PM 0
Share

I believe there's no need for the whole line yield return e.Current; or at least on my experiments I removed it and nothing wrong happened. Since this is actually a better solution than using StartCoroutine, because of static methods for one, I think it's quite relevant to make it shorter. ;-)

avatar image KvanteTore · Jan 04, 2013 at 06:59 PM 0
Share

you'll need the yield return e.Current line in order to actually give control back to the unity engine. without it the innermost coroutine will be run as if there were no yield statements at all.

avatar image KvanteTore · Jan 07, 2013 at 03:18 PM 1
Share

@Cawas, it turns out that IEnumerator.$$anonymous$$oveNext should be called before IEnumerator.Current is accessed. I've updated the question to reflect this.

avatar image cregox · Jan 07, 2013 at 03:53 PM 1
Share

Now, reading it as it is, without knowing the line was removed, that "edit" comment makes little sense... And your link is to IEnumerable, which is another thing and doesn't say anything about $$anonymous$$oveNext being first, unlike the proper link: http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx so, nice finding you did there! :-)

Show more comments

6 Replies

· Add your reply
  • Sort: 
avatar image
92
Best Answer

Answer by YardGnome · Apr 28, 2010 at 01:59 AM

You want:

IEnumerator Func2()
{
    yield return StartCoroutine( Func1() );
}

Func2 will not continue execution until the Func1 coroutine has completed. StartCoroutine() returns a Coroutine object, and this object can be returned by yield. Unity knows to finish executing the new Coroutine object before resuming the original function. You can use this technique to chain and nest coroutines.

For example: You want to create an object that does three in this order: fade in, move, and then shoot a gun. But since moving and shooting is very common in this game, you want to have a single function that simple does them both. You could write something like:

public void Start() { // Somebody starts the coroutine chain by calling: StartCoroutine( FadeAndMoveAndShoot() ); }

private IEnumerator FadeAndMoveAndShoot() { print( "FadeAndMove Start: " + Time.time ); yield return StartCoroutine( Fade() ); print( "FadeAndMove Middle: " + Time.time ); yield return StartCoroutine( MoveAndShoot() ); print( "FadeAndMove End: " + Time.time ); }

private IEnumerator Fade() { print( "Fade Start: " + Time.time ); yield return new WaitForSeconds( 2.0f ); print( "Fade End: " + Time.time ); }

private IEnumerator MoveAndShoot() { print( "Move Start: " + Time.time ); yield return new WaitForSeconds( 3.0f ); print( "Move Middle: " + Time.time ); yield return StartCoroutine( Shoot() ); print( "Move End: " + Time.time ); }

private IEnumerator Shoot() { print( "Shoot Start: " + Time.time ); yield return new WaitForSeconds( 1.0f ); print( "Shoot End: " + Time.time ); }

This will essentially output:

FadeAndMove Start: 0
  Fade Start: 0
  Fade End: 2
FadeAndMove Middle 2
  Move Start: 2
  Move Middle: 5
    Shoot Start: 5
    Shoot End: 6
  Move End: 6
FadeAndMove End: 6

The numbers are in seconds, and you can see that the original FadeAndMove courtine takes 5 seconds to complete. The desired behavior.

Comment
Add comment · Show 13 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image evelynliu · Apr 28, 2010 at 02:07 AM 0
Share

This is correct answer.

yield return StartCoroutine( Func1() );

avatar image KvanteTore · Apr 28, 2010 at 05:11 AM 0
Share

Sweet! Thank you very much :)

avatar image Ouchie · Feb 16, 2011 at 09:45 AM 1
Share

That's beautiful. What a great feature of the unity framework!

avatar image KvanteTore · Oct 05, 2011 at 12:33 PM 0
Share

I have since learned about some snags in this approach. If one of the coroutines happens to throw an exception, the execution of all the nested coroutines would stop abruptly. I've therefore fallen back on the approach of manually iterating through the nested IEnumerators.

avatar image cregox · Jan 04, 2013 at 01:51 PM 1
Share

This doesn't work on static methods! You'd need an instance, in which case it's probably better to implement Tore's manual iteration.

Show more comments
avatar image
2

Answer by duck · Mar 30, 2010 at 09:16 AM

You can do this by "manually" iterating through the 2nd coroutine's IEnumerator object that it returns, like this:

IEnumerator Func2() { Debug.Log("Func2 start");

 // manually iterate over Func1
 IEnumerator f1e = Func1();
 while (f1e.MoveNext()) {
     yield return null;
 }

 Debug.Log("Func2 complete");

}

IEnumerator Func1() { Debug.Log("Func1 start"); for (int i = 0; i < 10; i++) { Debug.Log("Func1: " + i); yield return null; } Debug.Log("Func1 complete"); yield break; }


Output:

Func2 start
Func1 start
Func1: 0
Func1: 1
Func1: 2
Func1: 3
Func1: 4
Func1: 5
Func1: 6
Func1: 7
Func1: 8
Func1: 9
Func1 complete
Func2 complete
Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image KvanteTore · Mar 30, 2010 at 09:38 AM 0
Share

yes, that is the same solution as I posted in my second code snippet, but it's a bit clunky. I was hoping I had overlooked something more elegant :)

avatar image KvanteTore · Mar 30, 2010 at 09:39 AM 0
Share

Only, you yield null from the Func2 ins$$anonymous$$d of passing on the yielded value from Func1. Is there a reason for that?

avatar image duck ♦♦ · Mar 30, 2010 at 09:45 AM 0
Share

Oh, sorry I completely overlooked your second snippet! Um, no reason really - The code in Func1 is executed by calling $$anonymous$$oveNext() so I'm not what difference (if any) it makes to yield null rather than the return value of Func1. I have a feeling that it only affects how long Unity waits before calling the coroutine again.

avatar image Calvin1602 · Jan 06, 2012 at 09:33 PM 2
Share

Actually, you should use while (f1e.$$anonymous$$oveNext()) { yield return f1e.Current; }. This allows to yield a new WaitForSeconds() from Func1, which is totally awesome.

avatar image Bunny83 · Jan 07, 2012 at 12:42 AM 0
Share

@Calvin1602: absolutely ;) I've just read the question and the answers. After reading this one i just wanted to add a comment like yours, but i've found your comment before i start writing ;)

avatar image
1

Answer by fabiopicchi · May 27, 2018 at 12:34 PM

I am very confused about this because

 IEnumerator Func1()
 {
     for (int i=0; i<10; i++)
     {
         yield return null;
     }
 }
 
 IEnumerator Func2()
 {
     //wait for the completion of Func1
     yield return Func1();
 }

Seems to be working just fine. Did Unity do something smart to detect IEnumerators being returned?

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · May 27, 2018 at 02:49 PM 1
Share

This has been changed recently. I don't remember which Untiy version actually introduced this "feature". I think it was about 5.6 but I'm not sure about that. Now if you yield an IEnumerator instance Unity will automatically start a new coroutine for the ienumerator and yield on that ins$$anonymous$$d

avatar image
0

Answer by qJake · Mar 30, 2010 at 08:23 AM

You may be over-thinking this...

yield return Func1();

You want this statement to "hold" until Func1() is completed, right? You don't need an IEnumerator to do that... standard, regular function calls (void Whatever();) block the current thread until they've completed.

So if you want to call Func1() and have it stop the current thread until it's complete, just... call it! Your first set of code, modified, would look like this:

void Func1() { for (int i = 0; i < 10; i++) { return; // return something here } return; // return something else here }

IEnumerator Func2() { //wait for the completion of Func1 Func1(); }

And then start a new coroutine with Func2();.

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image KvanteTore · Mar 30, 2010 at 08:30 AM 1
Share

Ah. That would work if Func1 is performs a simple computational task, but my Func1 is not quite as simple as this. It is waiting for external resources, which typically will not be available within one frame.

avatar image KvanteTore · Mar 30, 2010 at 08:31 AM 0
Share

And I don't want to block the entire Unity thread, I just want to halt the execution of Func2, coroutine-style.

avatar image qJake · Mar 30, 2010 at 04:30 PM 0
Share

Inside of a coroutine, as far as I understand it, you wouldn't block the entire Unity thread, just that one coroutine.

You could also just use standard C# threading ins$$anonymous$$d of mucking around in coroutines. Start a new thread that loads your external resource, and then call an event when it's completed. It's essentially an asynchronous action, and there is tons of example code and documentation online regarding threads and events with respect to C#. If you want me to, though, I can write a small set of example code for you. Just let me know.

avatar image Antitheory · Nov 30, 2010 at 09:47 PM 1
Share

It appears most Unity specific methods are not thread-safe though. You can't, for instance, create an object that derives from a $$anonymous$$onoBehaviour within a new and separate thread. If you call an event asynchronously (with BeginInvoke) it happens in a new thread and cant access Unity's API. I am using a separate thread to do background loading for some non-$$anonymous$$onoBehaviour objects and then polling for completion from a Unity Coroutine. Seems to be working so far. The downside is you cant actually create GameObjects in the background apparently.

avatar image
-1

Answer by Freezy · Feb 13, 2013 at 12:21 PM

You can also pass the IEnumerator from another function

This could allow all sorts of extensions, like a workaround for default parameters or classes or functions which cannot for whatever reason be called with a coroutine.

     [ContextMenu("Test")]
     void CallStuff()
     {
         StartCoroutine(Func2());
     }
 
     IEnumerator Func1(int a) 
     {
         Debug.Log("Func1 wait for " + a);
         yield return new WaitForSeconds(a);
         Debug.Log("Func1 done");
     }
     IEnumerator Func2()
     {
         Debug.Log("Func2");
         return Func1(2);
     }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

12 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

The name 'Joystick' does not denote a valid type ('not found') 2 Answers

Trouble with Iterative Coroutine 1 Answer

While loop yield lag? 1 Answer

NullReferenceException in StartCoroutine 1 Answer

Returning an IEnumerator as an int? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges