Unity 游戏在协程 运行 时停止

Unity game stops while coroutine running

Unity(角色移动和当前游戏场景中的一切)在从网络下载纹理时停止 url。 我正在使用 Socket.IO 进行在线多人游戏。

  1. void Start()
  2. 中使用 socket.emit 请求 'map info'
  3. 获取 'map info' 并使用 Instantiate()
  4. 创建地图实体
  5. 从url获取个人资料图像(纹理)并更改地图实体的纹理
IEnumerator DownloadImage(string MediaUrl, SpriteRenderer spr) {
    // check if url is malformed
    if (MediaUrl == null || MediaUrl.Contains("Null")) {
        yield break;
    }

    UnityWebRequest request = UnityWebRequestTexture.GetTexture(MediaUrl);
    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.DataProcessingError || request.result == UnityWebRequest.Result.ProtocolError)
        Debug.Log(request.error);
    else {
        Texture2D tex = ((DownloadHandlerTexture)request.downloadHandler).texture;

        spr.sprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), tex.width / 2f);
    }
}

有没有办法在下载纹理时保持游戏 运行 用户交互(角色移动或按钮操作)?

Unity co-routines (CR) 是一种 entry-level 在 Unity 主线程 上对一系列帧执行工作的危险形式。它们对于跨越一系列帧(例如 fading、explode-my-spaceship-after-5-secondsdeferring 的本质上是逻辑的 lerp 很有用驾驶舱显示更新.

CR 被分割并在主线程上多路复用,因此游戏中最慢的部分将成为单帧中对时间需求最大的 CR。在每帧 yield 期间执行任何冗长的操作都会减慢您的游戏速度,尤其是 I/O 操作 .

备选方案 - Unity 工作

相反,请考虑使用 Unity 'IJob',它允许在工作线程上执行操作,从而释放 Unity 线程来做它最擅长的事情,并且 不会很长 I/O 操作.

统一:

Use IJob to schedule a single job that runs in parallel to other jobs and the main thread. When a job is scheduled, the job's Execute method is invoked on a worker thread. More...

现在浪费整个线程可能会花费一生等待 I/O 完成可以说是对一个非常好的线程的浪费,但它肯定比阻塞主线程更好。

协程...成为龙

所以之前我提到 “危险” 关于 CR,原因是 three-fold:

  1. 在每秒调用多次的方法(例如 Update() 中触发 CR 时,请确保您 充分保护 StartCoroutine() 否则您最终可能会有数以万计的 运行 只到 运行 内存不足。这个在 Stack Overflow
  2. 上很常见
  3. 不要错误地认为它们是工作线程或其他不会减慢主线程速度的魔法
  4. 在许多方面,CR 在 Visual Basic.NET 中分别类似于 DoEventsApplication.DoEvents 和两者都会导致 re-entrancy 的严重情况(其中状态被破坏且不可预测,就像在 multi-threaded 应用程序中一样,但 没有线程 )

还有整个 Unity 是否在促进 IEnumeratoryield 的奇怪用法? 辩论。

我的另一个提示是,如果你需要一些 lerped 或延迟的东西并且没有做 I/O,考虑只做一个 class 并使用 Time.

例如in my flight sim,我只想每 100 毫秒左右更新一次驾驶舱显示。为此,我定义了一个 Delay class:

public class Delay
{
    private float _lastInterval;

    /// <summary>
    ///     The timeout in seconds
    /// </summary>
    /// <param name="timeout"></param>
    private Delay(float timeout)
    {
        Timeout = timeout;
        _lastInterval = Time.time;
    }

    public float Timeout { get; }

    public bool IsTimedOut => Time.time> _lastInterval + Timeout;

    public void Reset()
    {
        _lastInterval = Time.time;
    }

    public static Delay StartNew(float delayInSeconds)
    {
        return new Delay(delayInSeconds);
    }
}
.
.
.
private void Update()
{
    if (!_delay.IsTimedOut)
    {
        return;
    }

    // Do something time consuming
 
    _delay.Reset();
}