• 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
Question by Lahzar · Dec 22, 2015 at 03:30 PM · coroutinereflectionienumeratornameexists

Get IEnumerator from name or convert MethodInfo to IEnumerator

Title says it all: I am stuck on how I can return/store an IEnumerator, when I can only access its name. So I have a string, which is its name, and I want to end up with an IEnumerator.

I already know it exists, as I am getting the name of the coroutine from MethodInfo, but I need to store it for later use, and not start/invoke it!

EDIT: More info in comments

Comment

People who like this

0 Show 0
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

2 Replies

· Add your reply
  • Sort: 
avatar image
Best Answer

Answer by wibble82 · Dec 22, 2015 at 03:39 PM

Hi there

OK! After lots of comments below, I'm going to update this answer. What I originally wrote is still pretty relavent, so here it is in italics:

Calling the function returns the IEnumerator that unity uses to run your coroutine. You can call functions using method info using 'invoke': https://msdn.microsoft.com/en-us/library/a89hcwhh(v=vs.110).aspx So if you have the MethodInfo, you can call the function whenever you want. This will return an IEnumerator object. The IEnumerator object is simply the C# means to provide 'yield' functionality - i.e. a function that can execute in steps, yielding periodically. When you pass the IEnumerator to StartCoroutine, this simply hands it off to unity. Internally it creates a coroutine object, from which it repeatedly calls the enumerator until it finishes.

Now, onto the meat.

After lots of back and forth, my understanding of your problem is that, through whatever means, you have a function (you called ClassC) that currently has (or at least can be provided):

  • The name of a function from another class that returns an IEnumerable

  • Through reflection, you have got the MethodInfo for that function

  • You also say you have the 'script' it is on. Hopefully by that you mean the 'instance' of the script?

You have these 3 bits of info, because you want to extend the capabilities of unity's coroutine system to allow for pausing.

Your approach to this, which seems perfectly reasonable, is to create 1 generic unity coroutine that 'wraps' the calling of a 'coroutine function' provided by another class. Your generic coroutine function has pausing built in. This was your example, which I've not tested but seems reasonable:

 public IEnumerator RunRoutineManually() 
 {
     while (true)    
     {
         if (!routineIsPaused) 
         { //Imagine this is a bool controlled by ClassB
             if (routineHasStopped)
             { //This aswell
                 routine.Reset();
                 Destroy(this.gameObject);
                 yield return false;
             }
  
             yield return null;                      //Coroutine is paused!
         }
  
         routine.MoveNext();
         yield return routine.Current;
     }
 }

The question you are asking is: So I have my function name, my MethodInfo and my 'script', but my 'generic coroutine' needs an IEnumerator. Where the do I get it?!

So, to achieve your goal, all you actually need are:

  • The MethodInfo of your 'coroutine function'

  • The instance on which you want to run the coroutine - i.e. if the function is defined as part of ClassB, you will need the instance of classB on which you wish to run it. Hopefully this is what you meant by 'I have the script'

To explain things in detail, first I want to make sure you understand some bits and pieces (sorry if you already know this stuff!):

First, I've being saying 'coroutine function' so far, but there's really no such thing! This, is not a coroutine:

 public IEnumerator YieldReturnSomeBooleans() 
 {
     yield return false;
     yield return false;
     yield return false;
     yield return false;
     yield return false;
     yield return true;
 }

And neither is RunRoutineManually(). They are Enumerators. i.e. they are functions that the .net library uses to implement stuff like 'foreach' loops. Their critical property is that they can 'yield', return a result, and then be told to 'carry on', all by calling functions on the IEnumerator that they return. This I think you already understand.

Entirely independently, unity also implements a system called 'coroutines', which some languages would call 'fibres'. This system allows you to create functions that run independently of the stuff like 'Update', and have the ability to yield, then carry on next frame.

Naturally, the engineers at Unity decided the best way to expose coroutines to users was through Enumerators, because they provide the mechanism for writing a function that can yield, then be told to carry on. As a result we end up with the function:

 StartCoroutine(IEnumerator coroutine)

There is also a version that takes a string, but that's just a utility and I want to ignore it for now!

You use the unity system by calling your Enumerator function, and passing the IEnumerator it returns into StartCoroutine. From there Unity takes over, calling 'MoveNext' on the enumerator you provide it once per frame (or in other ways depending on what you return from it).

So:

 StartCoroutine(YieldReturnSomeBooleans());

Could be written:

 IEnumerator myenum = YieldReturnSomeBooleans();
 StartCoroutine(myenum);

Or if the coroutine function was part of another object, you could write:

 IEnumerator myenum = otherobject.YieldReturnSomeBooleans();
 StartCoroutine(myenum);

Similarly, you could store that 'myenum' value somewhere, pass it around, and later on call StartCoroutine.

Now, assuming you have your methodinfo and the instance on which you want to call it, you can do exactly the same as above. However instead of calling the YieldReturnSomeBooleans function directly, you can 'invoke' it via reflection:

 //I assume you have these somewhere?
 MethodInfo mymethodinfo = ?;
 object myobject = ?;
 
 //invoke the method that we know returns an IEnumerator on myobject, passing an array of 0 paramaters
 IEnumerator myenum = (IEnumerator)mymethodinfo.Invoke(myobject, new object[]{});
 
 //now start it as usual
 StartCoroutine(myenum);

So, going all the way back to your original function:

 public IEnumerator RunRoutineManually() 
 {
     //these are the values you presumably are storing in 'ClassC' now - not 'routine'
     MethodInfo mymethodinfo = ?;
     object myobject = ?;
 
     //we get 'routine' through invoke
     IEnumerator routine = (IEnumerator)mymethodinfo.Invoke(myobject, new object[]{});
 
     //everything else is just as you wrote it
     while (true)    
     {
         if (!routineIsPaused) 
         { //Imagine this is a bool controlled by ClassB
             if (routineHasStopped)
             { //This aswell
                 routine.Reset();
                 Destroy(this.gameObject);
                 yield return false;
             }
  
             yield return null;                      //Coroutine is paused!
         }
  
         routine.MoveNext();
         yield return routine.Current;
     }
 }

I hope that very long explanation was for the right thing. If not, hopefully it gets you on the right path.

-Chris

p.s. I haven't tested that code, so there might be a few compile errors. It's along the right lines though.

Comment
James2Games
vexe
Summit_Peak

People who like this

3 Show 3 · 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 Lahzar · Dec 22, 2015 at 05:11 PM 0
Share

Hey Chris,

So I think I should elaborate a bit more on why I am doing this. I want to pause a coroutine, generically/any kind of/non-specified, while there is another non-specififed coroutine running.

I have two scripts. One with a single class which contains the coroutines, and one with a namespace which includes two classes. The first class figures out which coroutine is supposed to be paused, and the second class which manually runs the coroutine.

This is whats inside ClassC, which is a separate class because ClassC must derive from MonoBehaviour, while ClassB must derive from another custom class, let's call it ClassD.

 class ClassC : MonoBehaviour {
         public IEnumerator routine;
         public string itsName;
 
         void Start() {
             //TODO: Get routine from itsName
             StartCoroutine(RunRoutineManually());
         }
 
         public IEnumerator RunRoutineManually() {
             while (true)    {
                 if (!routineIsPaused) { //Imagine this is a bool controlled by ClassB
                     if (routineHasStopped) { //This aswell
                         routine.Reset();
                         Destroy(this.gameObject);
                         yield return false;
                     }
 
                     yield return null;                      //Coroutine is paused!
                 }
 
                 routine.MoveNext();
                 yield return routine.Current;
             }
         }
     }

As you can see, I already am invoking the coroutine manually. But right now I only have the itsNames variable, and not the actual routine variable.

I also have the MethodInfo value of RoutineA, stored in ClassB, if this is necessary to access.

Its difficult to explain why, but you have to trust when I say that it has to work like this: FunctionB in ClassB returns RoutineA from ClassA, using MethodInfo. FunctionB then passes RoutineA as MethodInfo to FunctionC in ClassC.

avatar image wibble82 Lahzar · Dec 22, 2015 at 05:42 PM 0
Share

Hi Lahzar

I'll confess I'm a little confused - that'll take some thinking through. What is your actual question though? Certainly my answer is how you can get the IEnumertor to start a coroutine using MethodInfo. Perhaps that's not what you actually want to know?

p.s. I would recommend removing the big [UNANSWERED] from the question - we know it isn't answered as it isn't highlighted as 'accepted' in green. Big capital letters never help anybody :)

-Chris

avatar image Lahzar wibble82 · Dec 22, 2015 at 08:35 PM 0
Share

Hehe, the big [UNANSWERED] is just because Im paranoid people will think its one of those posts which has been answered, but OP, me, hasn't accept the answer anyways :S Will do, though, thanks.

I know its confusing! I find it really difficult to explain even the simplest of things, so me explaining this kind of advanced stuff has proven before to not get me anywhere, but I keep on trying!

I want to call a coroutines .MoveNext and .Current methods, without actually having direct access to the coroutine! All I have is its name, what script it is in, and I get all this from a MethodInfo call:

  BindingFlags bindAttr = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.OptionalParamBinding;
             MethodInfo methodInfo = type.GetMethod(routineName, bindAttr);
 //This is the methodInfo of RoutineA.
 //This code is in ClassB.


If you go check my previous questions, you can see I previously asked for your answer, actually. But I have been starting a coroutine using MethodInfo for a while now, problem is I now want to PAUSE my coroutine, while another method/routine/whatever, is running. I have everything in order, except I don't have an actual reference to the routine I want to pause!

So that is what I need! A variable which is the IEnumerator I want. All I have to get it is: The routines name. The routines containing script and gameobject etc. The MethodInfo which is the routine, but not an IEnumerator.

avatar image

Answer by jmonasterio · Dec 22, 2015 at 07:04 PM

Perhaps see:

http://twistedoakstudios.com/blog/Post83_coroutines-more-than-you-want-to-know

In the article it mentions:

Your coroutine can yield one of these in order to wait for another coroutine to finish before resuming execution.

Which sounds like what you said you want to do (in comments).

Comment

People who like this

0 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 Lahzar · Dec 22, 2015 at 08:43 PM 0
Share

Hey man!

So I understand your confusion, as I am really bad at explaining. But pausing a coroutine is not what I want to find out how to do, because I can already do it!

I actually followed the twisted oak studios video tutorial, and blog, which really helped! If I had been able to pass the entire coroutine as a parameter, it would all work. But the coroutine is passed as a string! That is a big factor here! That is how I get its name.

What I want: I want to generically pause any coroutine from this other script.

What do I think I need: I need to access the coroutines .MoveNext and .Current methods.

Why can't I already: I can't do this already because I don't have an IEnumerator variable. All I have is the routines name, what script it is located in, and a MethodInfo variable which is infact the coroutine... But not an IEnumerator. And I don't know how to convert or cast these types!

Thank you all for you feedback! Just ask if there is something about my question you don't understand, and I will try to elaborate futher! ~ Imre Angelo

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.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

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

Waiting twice inside coroutine (C#) 2 Answers

Why do you use Coroutines and how do you use them? 1 Answer

Execution order when Start is declared with IEnumerator return (as a Coroutine) 1 Answer

IEnumerator not waiting before relooping. 1 Answer

Coroutine doesn't work when called from another method. 3 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