Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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
1
Question by Tim-Michels · Oct 22, 2012 at 08:07 AM · coroutineeventyieldwait

Best way to wait for event inside Coroutine?

Let's say I have a coroutine which waits for the player to login. The function EndLoginSequence is subscribed to an event OnEndLogin. This code works just fine, but I was wondering if there's a way to wait for the event to get called inside the coroutine. It seems like a hassle to always have a bool variable that gets set inside a function. This code is the current way I'm doing this, but I would like a 'cleaner' and shorter way to wait for the event inside the coroutine.

 public event EventHandler OnEndLogin;
 bool IsLogginIn = false;

 IEnumerator WaitForLogin()
 {
     while (IsLoggingIn) yield return new WaitForEndOfFrame();
     ConfirmLogin();
 }
 
 public void EndLoginSequence()
 {
     IsLoggingIn = false;
 }

Any thoughts? Thanks in advance.

Comment
Add comment
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

3 Replies

· Add your reply
  • Sort: 
avatar image
0

Answer by JanWosnitza · Oct 22, 2012 at 09:04 AM

I would implement another C# event "OnLogIn" on the class that initiates the log-in and inside the event start the coroutine instead of testing "IsLoggingIn" every frame.

Comment
Add comment · Show 2 · 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 Tim-Michels · Oct 22, 2012 at 09:23 AM 0
Share

Then I can't do stuff while the user is loggin in. The purpose of the coroutine is to animate some stuff while loggin in, inside the coroutine. I just would need something like: while(!OnEndLogin.HasBeenInvoked)

avatar image JanWosnitza · Oct 22, 2012 at 10:17 AM 1
Share

I would put the animation-logic completely outside the login-logic: Call loginAniBehaviour.startOrSo() on OnLogIn. Call loginAniBehaviour.endOrSo() on OnEndLogIn.

Of course this wouldn't remove the bool field inside loginAniBehaviour, but makes your problem more specific to starting and ending an animation (which could also be done through Animation if a simple animation fits your needs) and let's the bool field "feel" much less dirty (try to say that 3 times in a row ^^).

BUT as with all program$$anonymous$$g, why to change logic as long as it perfectly fits your needs?

Or in other words: nope I don't think there isn't a way to get rid of the bool since it holds your ani state, it has to be somewhere :)

as of while(!OnEndLogin.HasBeenInvoked): this does not exist in C# because events can be fired multiple times

avatar image
0

Answer by andrew_pearce · Feb 29, 2020 at 07:27 AM

I had the same issue and found this topic. I ended with the following code inside coroutine:

     bool isEvenFired = false;
     someObject.OnSomeEven += () => {
       isEvenFired = true;
     }
     while (!isEvenFired)
       yield return new WaitForEndOfFrame();
     // here your code which should  be called withing coroutine only after certain even happened

I am using local variable as a switch to wait when certain even will occur to continue execution of cortoutine. I had a case with loading assets, when some other coroutine might be in the middle of loading AssetBundle so I had to wait when they will finish and then access it (since we are not allowed to load bundle more than once). I hope this code will save someone's time

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
avatar image
0

Answer by Bunny83 · Feb 29, 2020 at 11:15 AM

If you want a less performance hungry solution if you have many of such coroutines there's another little hacky solution which whould work pretty well. Though you would need a lot additional information about your coroutine. Specifically you need:

  • keep a reference to the IEnumerator of the coroutine so we can continue it later.

  • keep a reference to the monobehaviour you want to run the coroutine on so we can stop and continue it. This could be just the script the coroutine is located in.


The overall idea would be something like that:

 IEnumerator coroutine = null;
 MonoBehaviour host = null;
 bool halted = false;
 
 void StartRoutine()
 {
     coroutine = MyCoroutine();
     host = this;
     host.StartCoroutine(coroutine);
     halted = false;
 }
 
 object Suspend()
 {
     if (coroutine != null && host != null)
     {
         host.StopCoroutine(coroutine);
         halted = true;
     }
     return null;
 }
 
 public void ContinueRoutine()
 {
     if (coroutine != null && host != null)
     {
         host.StartCoroutine(coroutine);
         halted = false;
     }
 }
 
 
 IEnumerator MyCoroutine()
 {
     // do something
     yield return UsualStuff;
     
     // wait for event
     yield return Suspend();
     
     // continue work when the event happend.
 }


With this you can simply subscribe the ContinueRoutine() to any event you like. Whenever the coroutine calls "Suspend" the coroutine will actually be terminated. When "ContinueRoutine" is called a new coroutine is created which will resume the coroutine where it left off. Note that the "halted" flag isn't needed in general, but it's highly recommended. Without it calling ContinueRoutine while the coroutine is still running would create two coroutines running the same statemachine which would be a total mess and nothing works as it should.


Coroutines can always be interrupted and resumed that way. I even managed to serialize a coroutine to disk and resume it the next run. However that's a very complex approach and comes with several drawbacks. First you have to manually serialize all data that the IEnumerator contains through reflection so we can actually restore the object later. The second important thing is you always loose the last executed yield instruction. So if your coroutine currently waited on a Webrequest, a WaitForSeconds, ... that will in essence have no effect since the IEnumerator will simply continue after that statement when resumed.


Some warnings:
You only want to use such an approach when the event you're waiting for takes a long time and doesn't happen too often. For example user input. Starting / Restarting a coroutine will generate garbage so this is not a solution if your "event" is happening on a regular basis. In this case it's way better to just have the coroutine busy-waiting on a boolean.


For example for UI buttons I actually created a custom yield instruction which handled the registration of the button events and carried the boolean inside. However I just can't find it anywhere ^^ google is letting me down. If I find it I will cross link it.

edit

Found it (my WaitForUIButtons.class) ^^. I actually put it on the Unity wiki. This class could even be used without a coroutine just as a multiple-buttons-to-one-event solution. It will automatically create and register a seperate callback for each button and remove them after a button has been pressed. The class is designed to be reused to avoid unnecessary garbage generation.

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

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

Alternative for semaphores in Unity? 2 Answers

WaitUntil Combined With sqrMagnitude is not working? 1 Answer

Wierd issue with Coroutines? 2 Answers

C# Coroutine help 1 Answer

WaitForSeconds Question 2 Answers


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