- Home /
Returning an IEnumerator as an int?
So what I am trying to do is, when I call the method in another class, I want to get the int from the coroutine, but I have hit a road block on how to do this. As you can see below I tried to do it, but I just can't figure it out.
Eventually I want to store the int into a playerpref.
using UnityEngine;
using System.Collections;
public class TimerController : MonoBehaviour
{
private string url = "mywebsite"; //timer php url
public int unixTimeSeconds;
void Start ()
{
}
public void RequestTime()
{
StartCoroutine(GetTime());
}
public int ReturnTime(string urlwwwtext)
{
unixTimeSeconds = int.Parse(urlwwwtext);
return unixTimeSeconds;
}
public IEnumerator GetTime()
{
WWW urlwww = new WWW(url);
yield return urlwww;
if (string.IsNullOrEmpty(urlwww.error))
{
//Debug.Log("server time: " + urlwww.text); //gets current unix timestamp since January 1, 1970
//yield return urlwww.text;
ReturnTime(urlwww.text);
}
else
{
Debug.Log("could not connect to my website, retrying");
StartCoroutine(GetTime());
}
}
}
Answer by iwaldrop · Jul 10, 2014 at 04:41 AM
I like using callbacks for coroutines. The following is untested code, but demonstrates how to use a callback that takes a string as an argument, and how to do something with it.
public void RequestTime()
{
StartCoroutine(GetTime((text)=>
{
if (!string.IsNullOrEmpty(text))
unixTimeSeconds = int.Parse(text);
});
}
public IEnumerator GetTime(Action<string> callback)
{
WWW urlwww = new WWW(url);
yield return urlwww;
if (string.IsNullOrEmpty(urlwww.error))
{
if (callback != null)
callback(urlwww.text);
}
else
{
Debug.Log("could not connect to my website, retrying");
StartCoroutine(GetTime(callback));
}
}
This is great, but I think I was able to figure out a simpler solution, but it isn't quite there just yet. I think you can help. So in the GetTime method, how do I wait until it wwwurl has finished downloading?
Another solution would be to use a bool and check for it in the Update() method, but I suspect that this will be process intensive for no reason. Is there another way? Thanks.
public class TimerController : $$anonymous$$onoBehaviour
{
private string url = "myserver"; //timer php url
public int unixTimeSeconds;
public void RequestTime()
{
StartCoroutine(GetTime());
}
public int GetUnixTimeSeconds()
{
return unixTimeSeconds;
}
private void ReturnTime(string urlwwwtext)
{
unixTimeSeconds = int.Parse(urlwwwtext);
}
private IEnumerator GetTime()
{
WWW urlwww = new WWW(url);
yield return urlwww;
if (string.IsNullOrEmpty(urlwww.error))
{
//Debug.Log("server time: " + urlwww.text); //gets current unix timestamp since January 1, 1970
//yield return urlwww.text;
ReturnTime(urlwww.text);
}
else
{
Debug.Log("could not connect to my website, retrying");
StartCoroutine(GetTime());
}
}
}
public void GetTime()
{
var timer = GameObject.Find("Timer").GetComponent<TimerController>();
timer.RequestTime();
//how to wait for it to download then return it?
Debug.Log(timer.GetUnixTimeSeconds());
}
To wait for the WWW object to finish downloading, I typically do the following:
WWW www = new WWW(url);
while (!www.isDone)
yield return null;
Okay, so how would I do this if GetTime() was in another class?
Edit: Oh I think I understand now. Basically downloading it twice?
Edit2: So that didn't work, but what did work was
public IEnumerator GetTime()
{
var timer = GameObject.Find("Timer").GetComponent<TimerController>();
timer.RequestTime();
WWW www = new WWW(timer.url);
yield return www;
Debug.Log(timer.GetUnixTimeSeconds());
}
Could you tell me if there is anything wrong with this? Is this essentially downloading it twice?
Edit3: So after running the method multiple times, I find that it will output a duplicate and then when ran a 3rd time, it will change to it to the 3rd time, ins$$anonymous$$d of the second time. This happens and doesn't happen because of the download speed I think.
So ins$$anonymous$$d of calling it in another script, I just figure it would be easier to call it where the int is needed. Thanks for your help.
It doesn't matter where you do it, if you only have one WWW then you can be sure that you're only downloading the data one time. However, using a callback is really the way to go if you need data returned. It can be a simple int or a custom class; whatever Type you define as the Action (callback) parameter.
public static IEnumerator GetWWWAsync(string url, Action<WWW> callback)
{
if (callback == null)
Debug.LogError("You must supply a callback with GetWWWAsync");
WWW result = new WWW(url);
while (!result.isDone)
yield return null;
if (!string.IsNullOrEmpty(result.error))
Debug.LogError(string.Format("Error while downloading from {0}: {1}", url, result.error));
else
callback(result);
}
The above code will return only valid WWW objects. You can call it from any class, like this (assu$$anonymous$$g it's in a class called 'Utilities':
private int unixTimeSeconds;
void DownloadStuff()
{
StartCoroutine(Utilities.GetWWWAsync("www.google.com", (www)=>
{
time = int.Parse(www.text);
}));
}
And if you want to wait for the download, just make your downloading method a coroutine:
IEnumerator DownloadStuff()
{
yield return StartCoroutine(Utilities.GetWWWAsync("www.google.com", (www)=>
{
time = int.Parse(www.text);
}));
}
In closing, callbacks are a very good way to execute code after an async event is complete and, in C#, your callbacks can be full fledges closures, taking advantage of the current context of your code rather than necessitating additional methods and parameters (as you have done in the OP).
Your answer
Follow this Question
Related Questions
Waiting twice inside coroutine (C#) 2 Answers
How to create an Idle counter ? 2 Answers
IEnumerator's did not read "bool" after yield return new WaitForSeconds. 1 Answer
Why Won't My Coroutine Yield? 2 Answers