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’:
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.