【问题标题】:How to set variable inside of a Coroutine after yielding a webrequest产生网络请求后如何在协程内设置变量
【发布时间】:2019-02-01 02:50:44
【问题描述】:

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

这是我正在使用的 Scriptable Object Class。

[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));
    }
}

【问题讨论】:

  • 你在没有using 的情况下尝试过吗.. 我可能完全错了,但它也可能处理本地创建的变量img 和创建的精灵
  • @derHugo 是的,我没有使用 using 语句尝试过,但没有成功。我确实在没有 webrequest 的情况下进行了另一项测试,其中产量低于我设置变量的位置,并且在使用 lambdas 时效果很好。但我认为收益回报 webrequest 是让我感兴趣的。
  • 所以只要协程在运行,你就有正确的精灵?好吧,webrequest 是必不可少的,我猜想获取图像;)而产量是你使用它的方式,所以我不会为了调试而改变它;)我仍然认为Texture 本身可能以某种方式被处置,因此你的精灵没有纹理了吗?
  • 您是否尝试过返回纹理并在回调中创建本地 Sprite?
  • @derHugo 是的,我尝试只返回 Texture2D,然后在本地创建精灵,但是一旦离开协程,它就会变为 null。

标签: variables unity3d coroutine yield-return


【解决方案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 就会删除它,但 UnityWebRequest 创建的 Texture2D 可能表现不同。

比在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);
            })
        );
}

【讨论】:

  • 我会接受这个答案,因为它让我找到了正确的思路并解决了问题。我完全按照您所说的做了,但缩略图变量仍然为空。这让我开始思考协程是如何工作的,我基本上是在调用之后检查它,所以它在给出响应之前就执行了。我最终让一切正常工作的方法是将额外的代码也放入协程中,这样就解决了这个问题。非常感谢您的帮助。
  • @Timg 我希望你保留using 块。否则,您的 UnityWebRequestTexture 对象可能无法正确处理,并且您最终可能会泄漏资源,这可能会成为问题,具体取决于调用它的频率。 help page 在这一点上不是很清楚。
  • 好吧,现在我们知道错误实际上是我们看不到的代码的一部分,您可能应该回到您已经拥有的代码;)
  • 是的,我确实回到了 using 块,只是确保一切都在协程内并且一切正常。
【解决方案2】:

问题在于您的这部分代码:

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;
   }));
}

【讨论】:

  • 我已将循环更改为 for 循环而不是 foreach,如下所示: for (int i= 0; i { ((MovieTemplate)templates[index]).thumbnail = result; })); } 但是变量仍然返回为空。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-29
  • 2019-03-06
相关资源
最近更新 更多