I have a popup message box which is driven by a yield function. This yield waits until a button is hit (confirm or cancel). When a button is hit I want to retun the button type that was hit to the point at which the initial popup message box was called.
I know there's someting about co-routines only returning IEnumerators but I can't seem to get that working. ..and I certainly cant do returnValue= yield myCoroutine().
So how does this work?
A Unity Coroutine is a C# generator. Every time you call yield in a generator you are “returning” a value. That’s what they are intended to do, generate values. The problem isn’t about returning values from a Coroutine, because you’re doing that. The problem is getting access to those values that the Unity engine absorbs.
You do it by running the generator yourself instead of letting Unity do it, and exposing the yielded value using a public property.
Create a utility class for invoking coroutines…
public class CoroutineWithData {
public Coroutine coroutine { get; private set; }
public object result;
private IEnumerator target;
public CoroutineWithData(MonoBehaviour owner, IEnumerator target) {
this.target = target;
this.coroutine = owner.StartCoroutine(Run());
}
private IEnumerator Run() {
while(target.MoveNext()) {
result = target.Current;
yield return result;
}
}
}
Put a yield at the end of your coroutine with the value you want to return…
Invoke it and get the return value using the utility class…
CoroutineWithData cd = new CoroutineWithData(this, LoadSomeStuff( ) );
yield return cd.coroutine;
Debug.Log("result is " + cd.result); // 'success' or 'fail'
Notice you need to yield to cd.coroutine not cd. If you tried to yield to cd, the code would only wait one frame, even if the coroutine is not finished.
You can't directly return values from Coroutines because what Coroutines actually are is an enumerator which can be used e.g. in a foreach-loop (that's what these IEnumerators and the yield was originally created for). So, Unity executes a part of the Coroutine until it hits
yield return <something>;
(that's C# syntax, and I highly recommend using C# because it really gives you much more of an understanding of what's actually going on there).
When such a statement is hit, control is returned to the caller of the method (which is some part of the Unity engine that manages coroutines). Until Unity decides to "grab the next item from the enumeration" ... which depends on what you return (e.g. WaitForSeconds would let Unity wait an amount of frames until the given number of seconds have passed).
Coroutines are not really asynchronous but in many ways they appear to be and from the perspective of returning values, they are: How could you return a value to a piece of code that gets executed after the Coroutine was started? You'd have to "inject" the return value somehow back ... won't work ;-)
So what you usually do with asynchronous programming is to have the method that's asynchronously called (in this case your coroutine) write the relevant return values into a class so that others can read them on demand. For that purpose, you could use a member variable in your script that also implements the coroutine. In fact, that would be the simplest solution. Then you can simply poll for that value (e.g. in each Update) and act whenever the value has changed (or when there is a value at all).
If you already are in a coroutine and wants the result from a different coroutine, you do this:
public void Start()
{
StartCoroutine(DoingTheWork());
}
/// <summary>
/// The Coroutine who wants the result from another coroutine
/// </summary>
public IEnumerator DoingTheWork()
{
int result = 0;
yield return Run<int>(CalculationCoroutine(), (output) => result = output);
Debug.Log("The result is: " + result);
}
/// <summary>
/// The coroutine doing a calculation/action over time
/// </summary>
public IEnumerator CalculationCoroutine()
{
int result = 0;
for (int i = 0; i < 100; i++)
{
result += i;
yield return new WaitForSeconds(0.1f);
}
yield return result;
}
public static IEnumerator Run<T>(IEnumerator target, Action<T> output)
{
object result = null;
while (target.MoveNext())
{
result = target.Current;
yield return result;
}
output((T)result);
}
var buttonHit:bool=false;
var didConfirm:bool=false;
function OnGui(){
if(GUI.Button("Confirm")){
buttonHit=true;
didConfirm=true;
}
if(GUI.Button("Cancel")){
buttonHit=true;
didConfirm=false;
}
}
function DoWait(){
while(!buttonHit)
yield;
if(didConfirm)
print("user has confirmed action");
else
print("action was cancelled!");
}
EDIT: In this variant, the coroutine handles everything - so you don't initiate a coroutine just to wait for a button click. Instead, you're already inside the coroutine. (Note: Not included is the section making sure your GUI message box is visible only when you want it to be):
function HandleMessageBox(){
ShowMessageBox();
while(!buttonHit)
yield;
HideMessageBox();
if(didConfirm)
print("user has confirmed action");
else
print("action was cancelled!");
}
IEnumerator TryToSleep() {
var request = CountSheep();
yield return StartCoroutine(request);
int? result = request.Current as int?;
Debug.LogFormat("result: {0}", result);
}
IEnumerator CountSheep() {
yield return 99;
}
Since “IEnumerator.Current” is an object but “int” is non-nullable, the value returned should be “int?”.
Remember that only the last variable returned by “yield return” can be aceessed.
Notice that it will always wait for one more frame to get the value. In the following example, the 2 “Time.time” (the beginning time of current frame) are different.
IEnumerator TryToSleep() {
var request = CountSheep();
yield return StartCoroutine(request);
Debug.LogFormat("time out of coroutine: {0}", Time.time);
int? result = request.Current as int?;
Debug.LogFormat("result: {0}", result);
}
IEnumerator CountSheep() {
Debug.LogFormat("time in coroutine: {0}", Time.time);
yield return 99;
}
You can use “yield break” to stop the coroutine if you have already got the value.
Monolithic-post, but I have updated Ted and Spatanz version of the class for anyone that wants to return a constant flow of data over time. This version can also be stopped at any point by using Stop(). This version, however, should not be returned as " yield return ‘classreference’ " - but rather yield return null as shown below.
public interface ICoroutineExtra
{
IEnumerator Run();
void Stop();
}
public class CoroutineWithData<T> : CustomYieldInstruction , ICoroutineExtra
{
protected MonoBehaviour mono { get; private set; }
protected IEnumerator _target { get; private set; }
public T result { get; protected set; }
public Coroutine Coroutine { get; private set; }
protected bool iterator_processing = true;
public CoroutineWithData(MonoBehaviour owner_, IEnumerator target_)
{
_target = target_;
mono = owner_;
Coroutine = owner_.StartCoroutine(Run());
}
public IEnumerator Run()
{
while (_target.MoveNext())
{
result = (T)_target.Current;
yield return result;
}
iterator_processing = false;
}
public void Stop()
{
mono.StopCoroutine(Coroutine);
return;
}
public override bool keepWaiting
{
get
{
return iterator_processing;
}
}
};
Also, here’s the usage in a Coroutine:
while (class_name.keepWaiting)
{
//Capture Data, Do Stuff ; Example Below
(i.e. my_value = class_name.result;
ProcessValueInFunction(my_value) )
yield return null;
}
What about something like this? In the olden days, all we’d need to do is pass a pointer to the function, then wait for it to complete. Here’s a pattern I used:
You cannot use the default Co-Routine processor in unity to return a variable in the way your expecting. All the other methods either rely on a global variable or fire off a delegate to grab the variable.
There is a plug-in called “Co-Routine pro” that will return variables the way your expecting. Along with the ability to try/catch within the IEnumerator. And will give you a stack trace for your debugging purposes.
It is here at: Unity Asset Store - The Best Assets for Game Making
Just look at the screenshots, there are examples of how you return a variable even if they are deeply nested.
Just use CoRoutine Pro in the unity asset store, none of these solutions above work very well, or are even elegant. It replaces the coroutine processor with a more robust one that lets you return variables, trap on exceptions and works for editorapps.