【问题标题】:In Unity is a WWW class asynchronous or synchronous?Unity中的WWW类是异步的还是同步的?
【发布时间】:2023-03-13 19:45:01
【问题描述】:

我正在尝试了解如何在 Unity 中编写 WWW 调用。根据http://docs.unity3d.com/ScriptReference/WWW.html这里的描述,我可以检查 isDone 属性,但在同一页面上的示例中,它没有尝试检查 isDone。

我的问题是,如果我拨打 WWW 电话并且需要几秒钟才能回复,游戏不会冻结吗?

我想正确的代码是这样的,但是是这样吗?

StartCoroutine(WaitForResponse(myWWWObject));

private IEnumerator WaitForResponse(WWW aRequest ) {

    while ( ! aRequest.isDone )
        yield return aRequest;

}

在 aRequest 完成之前游戏会冻结吗?还是真的是异步的?

【问题讨论】:

  • 从示例中,我猜测yield return www; 行异步等待页面下载完成。
  • 在该示例中,它只是等待 www 请求完成(等待发生在“yield return www;”行)。您还可以使用这个新的 webrequest 来获得更多控制:docs.unity3d.com/ScriptReference/…
  • 如果返回的是 IEnumerator,它怎么知道它已经完成了......没有什么可以“测试”完成
  • 看来我上面的代码可以正常工作,但它是矫枉过正的;但只是一点点。 IsDone 用于测试主线程上的 WWW 完成情况,而 yield 用于协程。 yield 会暂停线程,直到答案返回,无论是成功还是失败。因此,在协程中,循环将在协程中进行一次迭代。

标签: asynchronous unity3d


【解决方案1】:

您需要了解协程 - Unity 的一项基本功能,它允许您编写长时间的代码函数(例如:比一帧更长),不会冻结您的游戏。

http://docs.unity3d.com/Manual/Coroutines.html

在 C# 中,您可以发现协程函数,因为它的返回类型为 IEnumerator。您可以通过 C# 关键字 yield 在代码中找到函数将暂停和继续的位置。

每个 MonoBehaviour 类都可以管理协程,如果你告诉它使用 StartCoroutine() 开始一个协程,那么 MonoBehaviour 将在每一帧(有时不止一次)调用协程,直到协程到达终点。

对于 WWW 类(支持协程),你只需要调用这个:

  WWW www = new WWW(url);
  yield return www;

您创建一个带有要检索的 URL 的 WWW 类,并且 MonoDevelop 协程管理器基本上会在每一帧中自动调用 yield,直到 www 对象说它已经完成(成功或失败)。

在此期间,您的游戏完全不会冻结。

【讨论】:

  • 在我看来,线程是冻结的东西,因为它是一个协程,它不会影响你的主进程。只是你的主进程必须知道线程被锁定并管理它(即在必要时将其关闭;例如停止所有协程)。
  • 协程在主线程中运行——这是它们的关键原因——你不需要担心多线程。协程应该设计成做很多小工作,然后通过yield放弃cpu。非常适合下载文件。对于无法轻易屈服的繁重计算,线程仍然是要走的路。
【解决方案2】:

WWW的链接文档页面中,以下方法是协程方法:

IEnumerator Start() {
    WWW www = new WWW(url);
    yield return www;
    Renderer renderer = GetComponent<Renderer>();
    renderer.material.mainTexture = www.texture;
}

yield 语句用于暂停执行并将控制权返回给 Unity,然后在下一个游戏帧中恢复执行。例如yield return null; 将导致在下一帧继续执行。此外,您可以使用派生自 YieldInstruction 类的类之一来扩展该行为,例如 WaitForSeconds

考虑 yield return new WaitForSeconds(1f); - 这将在 1 秒的游戏时间过去后恢复执行。 WWW 类以类似的方式工作。您可以使用该类的实例来产生执行,仅在下载完成后返回,无需手动轮询isDone 属性。

请记住,您只能在Coroutines 中屈服,我建议您阅读它们。如果您想在协程中执行 WWW 请求,那么您必须手动轮询 isDone 并在完成后继续。

回答最后一个问题:创建 WWW 类的实例时游戏会死机吗? - 不,该类异步工作。你可以在正常的更新/启动等函数中使用它,也可以在 Coroutines 中使用它。

【讨论】:

  • 协程是必经之路,因为您无法保证外部站点的响应能力。此外,即使使用响应式外部站点,网络延迟也会为请求增加大量时间,尤其是在编码不正确的情况下。
  • 这取决于场景。 WWW 类异步工作,因此它不会冻结主游戏循环线程,除非您将其包装在 while(!isDone) 循环中。
【解决方案3】:

如果我拨打 WWW 电话需要几秒钟才能回复,游戏不会死机吗? 不,游戏不会被冻结试试这个代码,你会看到日志"after corou"会立即显示,即使图片正在下载。

public class WWWDemo : MonoBehaviour {
    bool b = true;
    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update()
    {
        if (b) {
            StartCoroutine(ExampleCoroutine());
            Debug.LogWarning("after corou");
        }

    }

    IEnumerator ExampleCoroutine()
    {
        b = false;
        Debug.Log("Time : " + DateTime.Now);
        string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
        WWW www = new WWW(url);
        yield return www;
        Debug.Log("Time : " + DateTime.Now);
        Renderer render = gameObject.GetComponent<Renderer>();
        render.material.mainTexture = www.texture;
        Debug.Log("Time : " + DateTime.Now);

    }
}

但是在yield return www语句之后下一行的执行肯定会停止,它会等待图片下载,而Unity的其余事件将执行async

请单独考虑下面的示例。启动事件会执行它会等待但更新会持续运​​行的事件。

 IEnumerator Start() { 
        Debug.Log("Time : " + DateTime.Now);
        string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
        WWW www = new WWW(url);
        yield return www;
        Debug.Log("Time : " + DateTime.Now);
        Renderer render = gameObject.GetComponent<Renderer>();
        render.material.mainTexture = www.texture;
        Debug.Log("Time : " + DateTime.Now);
    }
void Update()
    {
            Debug.Log("update is running while start is waiting for downloading");        
    }

【讨论】:

  • 对你为什么要启动协程并在每一帧上向控制台发送垃圾邮件感到困惑
猜你喜欢
  • 2020-05-15
  • 2020-02-21
  • 2013-02-25
  • 2012-12-30
  • 2014-04-25
  • 1970-01-01
  • 1970-01-01
  • 2020-10-03
  • 1970-01-01
相关资源
最近更新 更多