- Home /
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".
why not just
yield return StartCoroutine();
in scriptA calling the webrequest coroutine in scriptB?
Answer by Bunny83 · Jul 16, 2013 at 08:02 PM
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 ;)
It works perfectly for me!!, just one thing... Is there anyway to call "WebRequest.Get" inside a static function? When i do that, i keep getting: "An object reference is required to access non-static member". Well i hope it works, cheers for any answers! :)
@bajskorven: Uhm, WebRequest.Get is a static function, so it can be called from anywhere. If you got an error it has to be related with your code. What's the "non static member" the compiler complains about? The WebRequest class actually doesn't have any non static members beside those inherited from $$anonymous$$onoBehaviour.
The only non static member that is used is StartCoroutine and there I use the singleton instance.
If you still have trouble, feel free to post your own question with your actual code. Don't forget to include a link to this question.
tnx very much! this example is very usefull for me!
just missing returning instance return m_Instance;
Answer by Jamora · Jul 16, 2013 at 07:07 PM
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.
Answer by scarletshark · Jul 16, 2013 at 06:56 PM
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.