Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity - need to return value only after coroutine finishes

I'm working on a game in unity and encountered an issue which I cannot solve. I'm connecting to a web server via standard WWW object and using a coroutine in order to execute a POST request.

The code in itself works, but I need to update a variable value and return that variable once the coroutine finishes, which I'm not able to do.

public int POST(string username, string passw)
{
    WWWForm form = new WWWForm();
    form.AddField("usr", username);
    form.AddField("pass", passw);

    WWW www = new WWW(url, form);

    StartCoroutine(WaitForRequest(www));

    //problem is here !
    return success_fail;
}

private IEnumerator WaitForRequest(WWW www)
{

    yield return www;
    if (www.error == null)
    {

            if(www.text.Contains("user exists"))
            {

                success_fail = 2;
            }
            else
            {
                success_fail=1;
            }
    } else {
        success_fail=0;
    }    
}

The coroutine updates the value of 'success_fail' with the relevant value. But the 'return success_fail;' line in the POST method runs before the coroutine finishes, which causes it to return a false value.

I've tried to use an additional coroutine but unsuccessfully, suppose that I had a error there as well. How I can return the 'success_fail' value only after the coroutine finishes?

Thanks.

like image 354
David Faizulaev Avatar asked May 15 '15 19:05

David Faizulaev


People also ask

Can coroutine return value?

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.

How do you keep coroutines from going inside?

To stop a coroutine from "inside" the coroutine, you cannot simply "return" as you would to leave early from an ordinary function. Instead, you use yield break . You can also force all coroutines launched by the script to halt before finishing.

Does yield break stop a coroutine?

Simply adding the yield break statement will end a Coroutine before it's finished. This can be useful for exiting a Coroutine as a result of a conditional statement, for example.

What is yield return Unity?

The yield return statement is special; it is what actually tells Unity to pause the script and continue on the next frame. There are a number of ways that can be used to yield return; one of which is to create an instance of the WaitForSeconds class.


2 Answers

Functions don't wait for coroutines before return, however you could use an Action to give some kind of return.

Put this in your Start function

WWW www = new WWW("http://google.com");

StartCoroutine(WaitForRequest(www,(status)=>{
    print(status.ToString());
}));

and add this.

private IEnumerator WaitForRequest(WWW www,Action<int> callback) {
    int tempInt = 0;
    yield return www;
    if (string.IsNullOrEmpty(www.error)) {
        if(!string.IsNullOrEmpty(www.text)) {
            tempInt = 3;
        }
        else {
            tempInt=2;
        }
    } else {
        print(www.error);
        tempInt=1;
    }
    callback(tempInt);
}

Give this a try, although the function can change a value it doesn't return a value and only has a single parameter. So in essence this isn't a solution for returning your coroutine however once received the int from the coroutine we are then able to justify what to do with it and even call other functions from within the callback.

StartCoroutine(WaitForRequest(www,(status)=>{
    print(status.ToString());
    Awake(); // we can call other functions within the callback to use other codeblocks and logic.
    if(status != 0)
        print("yay!");
    }
));

This might be of use to you. http://answers.unity3d.com/questions/744888/returning-an-ienumerator-as-an-int.html

like image 78
Grimmdev Avatar answered Oct 19 '22 06:10

Grimmdev


Only a coroutine can wait for another coroutine. Since you need to wait for the coroutine you started (WaitForRequest), it means you have to convert POST to be a coroutine and it won't be able to return int.

It looks like success_fail is a member variable, so if that's exposed to whoever is starts POST (as a coroutine), you wouldn't need to return it anyway.

public int success_fail

IEnumerator POST(string username, string passw)
{
    WWWForm form = new WWWForm();
    form.AddField("usr", username);
    form.AddField("pass", passw);

    WWW www = new WWW(url, form);

    yield return StartCoroutine(WaitForRequest(www));
}

private IEnumerator WaitForRequest(WWW www)
{
    yield return www;
    if (www.error == null)
    {
        if(www.text.Contains("user exists"))
            {
                success_fail = 2;
            }
            else
            {
                success_fail=1;
            }
    } else {
        success_fail=0;
    }    
}

Basically, if you want your code to "wait", it has to be a coroutine. You can't make a call that waits without blocking the whole engine (without some type of loop hack).

This thread gives a way where you could return the int from your coroutine if you really need to, but POST still can't be a blocking call...

http://answers.unity3d.com/questions/24640/how-do-i-return-a-value-from-a-coroutine.html

like image 23
Ted Bigham Avatar answered Oct 19 '22 07:10

Ted Bigham