产生webrequest后如何在协程内部设置变量

时间:2019-02-01 02:50:45

标签: variables unity3d coroutine yield-return

好的,我会尽我所能来解释这一点。我整天都在搜索并寻找解决此问题的方法,但似乎找不到它。我遇到的问题是,我有一个可脚本编写的对象列表,基本上可以用于自定义属性来创建游戏对象。我需要获得的那些属性之一是变成了精灵的Texture2D。因此,我在协程中使用UnityWebRequest,并且必须产生响应。得到响应后,我尝试设置变量。但是,即使在使用Lambda时,在我看来,如果我在结果之前返回return响应,也不会设置变量。因此,每次我在协程之后检查变量时,它都会返回null。如果有人可以用我在这里所缺少的东西启发我,那太好了!

这是我正在使用的脚本对象类。

[CreateAssetMenu(fileName = "new movie",menuName = "movie")]
public class MovieTemplate : ScriptableObject
{
    public string Title;
    public string Description;
    public string ImgURL;
    public string mainURL;
    public string secondaryURL;
    public Sprite thumbnail;
}

这里是协程的呼叫

foreach (var item in nodes)
{
    templates.Add(GetMovieData(item));
}

foreach (MovieTemplate movie in templates)
{
   StartCoroutine(GetMovieImage(movie.ImgURL, result => 
   {
       movie.thumbnail = result;
   }));
}

这是协程本身

IEnumerator GetMovieImage(string url, System.Action<Sprite> result)
{
    using (UnityWebRequest web = UnityWebRequestTexture.GetTexture(url))
    {
        yield return web.SendWebRequest();
        var img = DownloadHandlerTexture.GetContent(web);
        result(Sprite.Create(img, new Rect(0, 0, img.width, img.height), Vector2.zero));
    }
}

2 个答案:

答案 0 :(得分:1)

从您的需求来看,例程结束后似乎仍然以某种方式处理了纹理。我的猜测是它的发生是由于using块。

我将存储原始纹理参考

[CreateAssetMenu(fileName = "new movie",menuName = "movie")]
public class MovieTemplate : ScriptableObject
{
    public string Title;
    public string Description;
    public string ImgURL;
    public string mainURL;
    public string secondaryURL;
    public Sprite thumbnail;
    public Texture texture;

    public void SetSprite(Sprite newSprite, Texture newTexture)
    {
        if(texture) Destroy(texture);

        texture = newTexture;
        var tex = (Texture2D) texture;
        thumbnail = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.zero);
    }
}

因此,您也可以跟踪纹理本身,让它不被GC收集,但也可以在不再需要时销毁它。通常,Texture2D会在不再引用时被GC删除,但是Texture2D创建的UnityWebRequest的行为可能会有所不同。

比在webrequest中返回纹理并且不要使用using

IEnumerator GetMovieImage(string url, System.Action<Texture> result)
{
    UnityWebRequest web = UnityWebRequestTexture.GetTexture(url));

    yield return web.SendWebRequest();

    if(!web.error)
    {
        result?.Invoke(DownloadHandlerTexture.GetContent(web));
    }
    else
    {
        Debug.LogErrorFormat(this, "Download error: {0} - {1}", web.responseCode, web.error);
    }
}

最后使用它

for (int i = 0; i < templates.Count; i++)  
{
    int index = i;//If u use i, it will be overriden too so we make a copy of it
    StartCoroutine(
        GetMovieImage(
            templates[index].ImgURL,
            result => 
            {
                templates[index].SetSprite(result);
            })
        );
}

答案 1 :(得分:0)

问题出在代码的这一部分:

foreach (MovieTemplate movie in templates)
{
   StartCoroutine(GetMovieImage(movie.ImgURL, result => 
   {
       movie.thumbnail = result;//wrong movie obj
   }));
}

在这里,您将在回调结果到达之前放宽对电影对象的引用(由foreach覆盖)。

将其更改为以下内容:

foreach (int i=0;i<templates.Length;i++)  
{
   int index= i;//If u use i, it will be overriden too so we make a copy of it
   StartCoroutine(GetMovieImage(movie.ImgURL, result => 
   {
        templates[index].thumbnail = result;
   }));
}