【Unity】中的异步加载与分帧加载.
有机紫菜
2024年07月30日 19:07

Unity中加载一些东西时候.比如场景或者大量预制体的情况.

有几种常用的平滑加载的方法.

Unity6里好像有一种基于Streaming形式的地图分块流形式加载技术,暂时没看到技术细节.

  • 分帧平滑加载资源---根据性能,把资源逐个加载进来.分散性能消耗到每一帧.一般用在背包或者是,图标,物品比较多的情况,非整个场景的情况.

  • 异步加载.---后台进程加载资源.不会阻塞主线程.---注意,有人提到图片的格式,如果选择RGB24模式可能会导致异步加载也卡,RGBA 32则不会卡.---可能和GPU支持有关如果异步加载AssetBundle,不压缩的情况下会最快,注意是否和LZ4的压缩格式有关.

  • 有时候加载时候性能消耗还好,主要是实例化时候Object显示的时候,会导致卡顿,所以加载时候可以直接加载(INSTANTIATE)所有,但是显示的时候,SetActive的时候,进行平衡处理.

Unity中的异步加载(Asynchronous Loading)和分帧平滑加载(Load Smoothing Across Frames)虽然都是为了减轻游戏运行时的性能压力,但它们的原理和用途有所不同。

异步加载(Asynchronous Loading)

  • 原理:异步加载允许游戏在后台加载资源,而不会阻塞主线程。这意味着游戏可以在加载新场景或资源的同时继续运行,从而不会造成明显的停顿或冻结。

  • 实现:Unity 提供了AsyncOperation类和SceneManager.LoadSceneAsync方法,用于异步加载场景。这些操作通常涉及以下步骤:

    1. 开始加载操作,通常是通过调用LoadSceneAsync

    2. 在后台线程中加载资源。

    3. 通过AsyncOperationprogress属性来监控加载进度。

    4. 加载完成后,通常会有一个回调函数来处理后续逻辑。

分帧平滑加载(Load Smoothing Across Frames)

  • 原理:分帧加载是一种优化手段,它通过将加载工作分散到多个帧中来减少单帧的性能开销。这种方法并不一定是异步的,但它确保了每帧的处理时间保持在一个合理的范围内,从而避免帧率下降。

  • 实现:分帧加载通常是通过以下步骤手动实现的:

    1. 评估每帧可以处理的加载量。

    2. 在每帧的开始或结束时,加载一定量的资源。

    3. 通过检查加载进度或剩余工作来决定是否继续在下一帧加载。

对比

  • 目的:异步加载主要是为了不阻塞主线程,而分帧加载主要是为了平衡每帧的工作量。

  • 线程:异步加载可能涉及后台线程,而分帧加载通常在主线程上进行,但是将工作分散到多个帧中。

  • 使用场景:异步加载适用于大型资源的加载,如整个场景或大量的数据,而分帧加载适用于可以在多个帧中逐步加载的资源,以避免帧率下降。

总的来说,虽然两者都是为了优化加载性能,但它们的实现方式和应用场景是不同的。开发者会根据具体需求选择最合适的加载策略。

下面是一个异步加载场景的例子.主要使用UnityEngine.SceneManagement;中的异步加载的形式.

代码块
C#
自动换行
复制代码
SceneManager.LoadSceneAsync((int)SceneIndex.Main, LoadSceneMode.Additive);
复制成功

下面是一个分帧加载的范例

代码块
C#
自动换行
复制代码
public class SmoothLoader : MonoBehaviour
{
    public GameObject[] prefabs; // 预制体列表
    public int objectsPerFrame = 1; // 每帧加载的物体数量
    //读取频率
    public int frameInterval = 15;
    private Queue<GameObject> prefabQueue = new Queue<GameObject>();


    [Button("Load")]

    void LoadTest()
    {
        // 将所有预制体加入队列
        foreach (GameObject prefab in prefabs)
        {
            prefabQueue.Enqueue(prefab);
        }

        // 开始分帧加载物体
        StartCoroutine(LoadObjects());

    }


    IEnumerator LoadObjects()
    {
        while (prefabQueue.Count > 0)
        {
            //显示加载的进度
            Debug.Log("加载进度" + prefabQueue.Count);
            // 加载指定数量的物体
            for (int i = 0; i < objectsPerFrame; i++)
            {
                if (prefabQueue.Count > 0)
                {
                    GameObject spawned;
                    GameObject prefab = prefabQueue.Dequeue();
                    spawned = Instantiate(prefab, transform);
                    spawned.SetActive(true);
                    spawned.SetActive(false);
                    //实例化之后可以,setActive,然后再SetInActive.
                }
                else
                {
                    //如果队列已经为空,结束协程
                    yield break;
                }
            }

            
            for (int i = 0; i < frameInterval; i++)
            {
                // 等待下一帧
                yield return null;
            }
        }
    }
}
复制成功