Unity--异步加载资源

发布于:2024-07-04 ⋅ 阅读:(47) ⋅ 点赞:(0)

Unity–异步加载资源

0. 异步加载资源简介

在Unity中,异步加载资源是一种优化游戏性能的重要手段,特别是在处理大型资源或需要平滑过渡的场景时。以下是关于Unity中异步加载资源的内容总结:

1. 为什么使用异步加载

  • 避免卡顿:异步加载可以在不冻结游戏主线程的情况下加载资源,从而避免游戏过程中的卡顿。
  • 提高性能:在加载资源的同时,游戏可以继续运行,提高整体性能。

2. 异步加载方法

  • Resources.LoadAsync:与Resources.Load类似,但以异步方式加载资源。例如,Resources.LoadAsync<GameObject>("Cube")

3. 使用Resources.LoadAsync

  • 基本用法Resources.LoadAsync返回一个ResourceRequest对象,通过该对象可以检查加载进度和获取加载结果。因为加载资源分为两个阶段:加载中和加载完毕

  • 示例

    public class LoadSource : MonoBehaviour
    {
        // 异步加载数据
        // 加载图片
    
        private Texture texture;
    
        private void Start()
        {
            MyAsychonizedLoad();
        }
        void MyAsychonizedLoad()
        {
            // 异步加载--会消耗帧, 所以有很多资源的时候需要写一个加载的进度条
            ResourceRequest re = Resources.LoadAsync<Texture>("Pictures/Picture");
            // 是否加载结束
            re.completed += ResourceCompleted;
            // 打印帧数
            Debug.Log(Time.frameCount);
        }
       
        // 加载完毕回调函数
        private void ResourceCompleted(AsyncOperation obj)
        {
            Debug.Log("资源加载完毕");
            // 打印帧数
            Debug.Log(Time.frameCount);
            // 对资源赋值
            // 先类型转换 在赋值
            ResourceRequest rq = obj as ResourceRequest;
            if (rq != null)
            {
                texture = rq.asset as Texture;
            }   
        }
    
        // 测试加载好的图片
        private void OnGUI()
        {
            if (texture != null) // 加载中的时候texture还是nuLl的
            {
                GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), texture);
            }
            
        }
    }
    
    

4. 使用协程来异步加载资源

  • 为什么使用协程来加载资源, 因为协程虽不是线程但是轻量级的线程,可以分担主线程的压力.
  • 为什么可以用协程来异步加载资源. 因为异步加载资源ResourceRequest和 协程Coroutine都有继承了YieldInstruction这个类. 由于加载资源是从加载中到加载完毕,是需要消耗一定的时间,这个时间我们不知道是多少,这取决于内存的大小和资源的大小. 而yeild return紧紧跟着的内容可以是 null 1 2 自定类, 等待时间等. 因此,利用等待时间来加载资源.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LoadResourcesAsychonized: MonoBehaviour
{
    // 异步加载数据 -- 加载图片
    private Texture texture;

    private void Start()
    {
        // 异步加载--协程模式加载
        StartCoroutine(LoadOver());
        StartCoroutine(LoadOverDetial());
    }

    // 使用协程加载资源
    IEnumerator LoadOver()
    {
        // 加载资源--图片
        ResourceRequest rq = Resources.LoadAsync<Texture>("Pictures/Picture");
        // ResourceRequest翻译为请求的资源-
        //该资源有很多信息: 是否加载完毕,加载优先级,当前加载进度,加载的资源,场景加载好就激活该资源(bool allowSceneActivation)
        // 打印帧数
        Debug.Log(Time.frameCount);
        // 加载完毕
        yield return rq;
        // 加载完毕继续执行, 为什么yield return rq呢? 因为ResourceRequest继承了AsyncOperation
        // 而AsyncOperation 继承了 YieldInstruction 且协程Coroutine中也是继承了 YieldInstruction
        // 所以大家继承了同一个类 当你使用 yield return rq; 时,
        // 你实际上是在告诉协程:“在这里暂停执行,直到 rq 这个异步操作完成。”
        // 这意味着协程会在这里等待,直到资源加载完成。
        // 一旦资源加载完成,协程就会从 yield return 语句的下一条语句继续执行。

        // 打印帧数
        Debug.Log(Time.frameCount);
        // 资源加载完毕后,继续执行
        texture = rq.asset as Texture;  // 将资源转换为我们需要资源的类型
    }

    // 协程异步加载详解
    IEnumerator LoadOverDetial()
    {
        // 加载资源
        ResourceRequest rq = Resources.LoadAsync<Texture>("Pictures/Picture");
        // 打印帧数
        Debug.Log(Time.frameCount);

        // 还可以获得加载的进度,是否加载完毕等数据
        while (!rq.isDone)
        {
            Debug.Log("资源加载优先级" + rq.priority);
            Debug.Log("资源加载进度" + rq.progress);
            yield return null;
        }
        texture = rq.asset as Texture;
    }

    // 测试加载的资源--屏幕上绘制图片
    private void OnGUI()
    {
        if (texture != null)
        {
            GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), texture);
        }
        
    }
}

5. 提取异步加载为单例类

现在需要使用一个异步加载资源的单例类, 其他类中可以直接调用该方法实现加载资源,不用手动写回调函数或者自己开协程加载资源.

知识点: 单例 + 异步加载 + 协程 + 委托/事件

单例类的基本思路:

  1. 传入要加载的资源的名称/路径,以及资源的类型
  2. 利用协程加载资源
  3. 资源加载好了要转换为对应的类型
  4. 转化好的资源需要返回给调用的地方
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class ResourceLoadManager : MonoBehaviour
{
    // 单例异步加载数据管理器--给外部一个方法用于加载资源

    // private static ResourceLoadManager instance = new ResourceLoadManager();
    private static ResourceLoadManager instance;
	// 单例部分(继承自MonoBehaviour和不继承的)
    private ResourceLoadManager() { }
    public static ResourceLoadManager Instance 
    {
        get
        {
            return instance;
        }
    }
    private void Awake() // 继承自MonoBehaviour的类,需要使用Awake来初始化数据
    {
        instance = this;
    }

    // 普通异步加载 + 回调函数(用来资源加载完毕时候的逻辑,赋值之类的)
    public void LoadResourceAsynchionzed<T>(string sourceName, UnityAction<T> callBack) where T : Object
    {
        // 加载资源
        ResourceRequest rq = Resources.LoadAsync<T>(sourceName);
        // 加载完毕调用回调函数
        rq.completed += (a) => // 这里的a实际上是AsyncOperation的实例 拉姆达函数
        {
            // 调用外部的回调函数 -- 转换资源
            callBack((a as ResourceRequest).asset as T);
            // 将加载好的资源通过外部回调函数返回
            Debug.Log("资源转换成功,并已返回给对象");
        };
    }
    
    // 使用协程加载
    public void LoadResourceAsynchionzedCoroutine<T>(string sourceName, UnityAction<T> callBack) where T : Object
    {
        // 开启协程
        StartCoroutine(LoadSource<T>(sourceName, callBack));
    }

    // 协程
    IEnumerator LoadSource<T>(string sourceName, UnityAction<T> callBack) where T: Object
    {
        // 加载资源中
        ResourceRequest rq = Resources.LoadAsync<T>(sourceName);
        yield return rq;
        // 资源加载好了--转换资源
        callBack(rq.asset as T);
    }
}

在需要加载资源的地方调用单例中的加载资源的方法即可

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LoadResourcesTest : MonoBehaviour
{
    private Texture texture; // 图片资源

    private void Start()
    {
        // 调用加载函数 -- 拉姆达表达式
        /*        ResourceLoadManager.Instance.LoadResourceAsynchionzed<Texture>("Pictures/Picture", (obj) =>
                {
                    texture = obj;
                });*/

        // 普通单例异步加载 -- 加载图片资源
        // ResourceLoadManager.Instance.LoadResourceAsynchionzed<Texture>("Pictures/Picture", OnpictureLoaded);
        // 协程单例异步加载-- 加载图片资源
        ResourceLoadManager.Instance.LoadResourceAsynchionzedCoroutine<Texture>("Pictures/Picture", OnPictureLoaded);
    }

    private void OnPictureLoaded(Texture loadedTexture)
    {
        texture = loadedTexture;
    }

    // 测试
    private void OnGUI()
    {
        if (texture != null)
        {
            GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), texture);
        }
    }
}

6. 注意事项

  • 异步加载时应确保资源加载完成后再进行操作。
  • 注意资源的卸载,避免内存泄漏。

7. 结论

异步加载资源是Unity游戏开发中的重要技巧,能够显著提升游戏性能和用户体验。开发者应根据项目需求选择合适的异步加载方法。


网站公告

今日签到

点亮在社区的每一天
去签到