Waiting for a value to return from another script using coroutines?

Hey guys, I’ve got 2 C# scripts, script A is trying to get a value from script B using “GetComponent”. The problem I’m facing is that that script B is getting that value from a .php script that is accessing a mySQL database and takes a second to get it’s value, thus returning a null as the variable I’m trying to access hasn’t been set yet. I’m wondering if there is a way for script A to yield for a response from script B without waiting for a set amount of time using “WaitForSeconds”.

For web requests you could use something like this:

// C#
// WebRequest.cs
public class WebRequest : MonoBehaviour
{
    private static WebRequest m_Instance = null;
    public static WebRequest Instance
    {
        get
        {
            if (m_Instance == null)
            {
                m_Instance = (WebRequest)FindObjectOfType(typeof(WebRequest));
                if (m_Instance == null)
                    m_Instance = (new GameObject("WebRequest")).AddComponent<WebRequest>();
                DontDestroyOnLoad(m_Instance.gameObject);
            }
            return m_Instance;
        }
    }
    public static Coroutine Get(string aURL, System.Action<bool, string> aCallback)
    {
        return Instance.StartCoroutine(_GetRequest(aURL, aCallback));
    }
    public static Coroutine Post(string aURL, WWWForm aForm, System.Action<bool, string> aCallback)
    {
        return Instance.StartCoroutine(_PostRequest(aURL, aForm, aCallback));
    }

    private static IEnumerator _GetRequest(string aURL, System.Action<bool, string> aCallback)
    {
        WWW request = new WWW(aURL);
        yield return request;
        if (string.IsNullOrEmpty(request.error))
        {
            if (aCallback != null)
                aCallback(true, request.text);
        }
        else
        {
            if (aCallback != null)
                aCallback(false, request.error);
        }
    }
    private static IEnumerator _PostRequest(string aURL, WWWForm aForm, System.Action<bool, string> aCallback)
    {
        WWW request = new WWW(aURL, aForm);
        yield return request;
        if (string.IsNullOrEmpty(request.error))
        {
            if (aCallback != null)
                aCallback(true, request.text);
        }
        else
        {
            if (aCallback != null)
                aCallback(false, request.error);
        }
    }
}

With this class you can start a webrequest and you can pass a callback to the function which is called when the request has finished. The callback has two parameters, a bool and a string. The bool indicates success and the string is either the response or the error text.

This class can be used from everywhere since it’s a singleton. Just do this:

WebRequest.Get("http://www.google.com", (success, text) =>{
    if (success)
    {
        Debug.Log("Success: " + text);
        // Do something with text
    }
});

Since the function returns the Coroutine object you can also wait in a coroutine like this:

IEnumerator Start()
{
    string result = "";
    yield return WebRequest.Get("http://www.google.com", (success, text) =>result = success?text:"");
    Debug.Log("Done: " + result);
}

Note: I just rewrote the class here in UA so i didn’t test the class, but i use a similar implementation in my projects :wink:

Because of the asynchronous nature of this behavior you’re trying to model, I would think it’s best to use events. However, Unity has a similar system, without the coding hassle; SendMessage. If you have numerous, or even lots of this kind of behavior, I would suggest using events, as SendMessage is quite costly and could cause lag.

I’m thinking something along these lines for your program logic: as soon as your Script B gets its value, it sends a message to Script A notifying it’s ok to read the value.

Hmm. I’m wondering if you could use a different method. Maybe you could do

while (foo == null){
     yield return new WaitForSeconds(0.2f);
}

The problem with this is that it could loop infinitely if foo remains null. So perhaps instead use a for loop, and after a few loops, return an error.