• Unity3D中的线程与协程


    线程 

      Unity3D是以生命周期主线程循环进行游戏开发。

      Unity3D中的子线程无法运行Unity SDK(开发者工具包,软件包、软件框架)跟API(应用程序编程接口,函数库)。

      限制原因:大多数游戏引擎都是主循环结构,游戏中逻辑更新和画面更新的时间点要求有确定性,必须按照帧序列严格保持同步,否则就会出现游戏中的对象不同步的现象。虽然多线程也能保证这个效果,但是引用多线程,会加大同步处理的难度与游戏的不稳定性。

      但是多线程也是有好处的,如果不是画面更新,也不是常规的逻辑更新(指包括AI、物理碰撞、角色控制这些),而是一些其他后台任务,比如大量耗时的数据计算、网络请求、复杂密集的I/O操作,则可以将这个独立出来做成一个工作线程,这需要写Unity游戏的Native扩展。

    协程

      对于Unity3D,它是生命周期主线程循环的设计,它更倾向于使用Time slicing(时间分片)的Coroutine(协程)去完成异步任务,融合到生命周期中。

      线程是操作系统级别的概念,现代操作系统都支持并实现线程,线程的调度对应用开发者是透明的,开发者无法预期某线程在何时被调度执行。基于此,一般那种随机出现的BUG,多与线程调度相关。

      而协程Coroutine是编译器级别的,本质是一个线程时间片去执行代码段。它通过相关的代码使得代码段能够实现分段式的执行,显式调用yield函数后才被挂起,重新开始的地方是yield挂起的位置,每一次执行协程会跑到下一个yield语句。协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

      在Unity3D中,协程是可自行停止运行 (yield),直到给定的 YieldInstruction 结束再继续运行的函数。协程 (Coroutines) 的不同用途: ·

           (1) yield return null - 这一帧到此暂停,下一帧再从暂停处继续,常用于循环中。

           (2) yield return new WaitForEndOfFrame - 等到这一帧的cameras和GUI渲染结束后再从此处继续,即等到这帧的末尾再往下运行。这行之后的代码还是在当前帧运行,是在下一帧开始前执行,跟return null很相似。

           (3) yield return new WaitForFixedUpdate - 在下一次执行FixedUpdate的时候继续执行这段代码,即等一次物理引擎的更新。

           (4) yield return new WaitForSeconds(3.0f) - 等待3秒,然后继续从此处开始,常用于做定时器。

           (5) yield return WWW - 等待直至异步下载完成。

           (6) yield return StartCoroutine(methodName) - 等待另一个协程执行完。这是把协程串联起来的关键,常用于让多个协程按顺序逐个运行。

      (7)yield break - 直接跳出协程,对某些判定失败必须跳出的时候,比如加载AssetBundle的时候,WWW失败了,后边加载bundle没有必要了,这时候可以yield break跳出。

      值得注意的是 WaitForSeconds()受Time.timeScale影响,当Time.timeScale = 0f 时,yield return new WaitForSecond(x) 将不会满足。

      以下为Unity3D的生命周期循环图

    c#代码示例

    Unity3D使用协程常需要用到辅助类Stopwatch,提供一组可用于准确地测量运行时间的方法和属性。

    private Stopwatch frameStopwatch;//用来记录上一帧结束到现在所用的时间
    private float targetFrameDuration;//自定义的每帧持续时间,防止协程过度消耗线程时间片
    private void Awake()
    {
        frameStopwatch = new Stopwatch();
    }
    
    void Update()
    {
        //计算每一帧所用的时间Start()之后Elapsed会一直增加,Stop()之后Elapsed的值就不变
        frameStopwatch.Stop();
        frameStopwatch.Reset();
        frameStopwatch.Start();

      if(ChunkUpdateList.Count > 0)
      {
        StartCoroutine(ProcessChunkQueueLoop());//启动协程处理ChunkUpdateList
      } }

    private IEnumerator ProcessChunkQueueLoop()
    {
        while (ChunkUpdateList.Count > 0)
        {
            ProcessChunkUpdateList();//每次处理ChunkUpdateList中的一个数据
            if (frameStopwatch.Elapsed.TotalSeconds >= targetFrameDuration)//这一帧已经运行的时间frameStopwatch.Elapsed.TotalSeconds已经超过自定义每帧的时间targetFrameDuration,则挂起直到这一帧结束再运行
            {
                yield return new WaitForEndOfFrame();
            }
        }
    }

    引用:

      (1)游戏引擎Unity中的单线程与多线程

    其他:

      (1)游戏主循环

      (2)3D引擎多线程:渲染与逻辑分离

      

  • 相关阅读:
    PHP基础1
    U2-Net网络学习笔记(记录)
    C++贪吃蛇游戏
    实习期间学习基础学习整理
    week 2020.1.10-2020.1.15
    week 2021.1.04-2021.1.08
    week 2020.12.21-2020.12.31
    周记 week 2020-12.14-12.18
    几种读取图片和标签的方法
    图像风格转换(Style Transfer | 风格迁移综述)
  • 原文地址:https://www.cnblogs.com/DonYao/p/8571981.html
Copyright © 2020-2023  润新知