Unity异步加载图片并创建预制体(不阻塞主线程)

项目中遇到需要加载某个文件夹下的内容,但是数量很多,需要在场景开始前就加载完成。这个时候我们可以加一个加载页面,利用协程和异步方法、后台线程去执行这个操作,当读取和创建操作完成后再将加载页面关闭。(这个方法不会阻塞主线程,但是加载页面会比较卡顿)

首先,讲一下思路:

        1.先获取文件夹下的所有文件的路径,存起来;

        2.使用协程和for循环去执行创建图片和创建预制体的方法;

        3.协程内循环执行异步方法,注意异步方法是调用线程,并非主线程,所以一些关于UI操作的任务我们需要用一个单例类,用于在主线程中调度。

接下来,请看代码:

UnityMainThreadDispatcher 是一个单例类,用于在主线程中调度操作:

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

// UnityMainThreadDispatcher 是一个单例类,用于在主线程中调度操作。
public class UnityMainThreadDispatcher : MonoBehaviour
{
    private static readonly Queue<System.Action> _executionQueue = new Queue<System.Action>(); // 创建一个队列用于存储需要在主线程执行的操作

    
    public void Update()
    {
        lock (_executionQueue) // 锁定队列,确保线程安全
        {
            while (_executionQueue.Count > 0) // 当队列中有操作时
            {

                _executionQueue.Dequeue().Invoke(); // 执行队列中的操作
            }
        }
    }

    private static UnityMainThreadDispatcher _instance = null; // 单例实例

    public static bool Exists()
    {
        return _instance != null; // 检查实例是否存在
    }

    public static UnityMainThreadDispatcher Instance()
    {
        //if (!Exists())
        //{
        //    throw new System.Exception("UnityMainThreadDispatcher is required to run."); // 如果实例不存在,抛出异常
        //}
        return _instance; // 返回实例
    }

    public void Awake()
    {
        if (_instance == null) // 如果实例不存在
        {
            _instance = this; // 设置实例为当前对象
            //DontDestroyOnLoad(gameObject); // 确保该对象在场景切换时不被销毁
        }
        else if (_instance != this) // 如果实例已经存在且不是当前对象
        {
            Destroy(gameObject); // 销毁当前对象
        }
    }

    public void Enqueue(System.Action action)
    {
        lock (_executionQueue) // 锁定队列,确保线程安全
        {
            _executionQueue.Enqueue(action); // 将操作加入队列
        }
        
    }
}
-----------------------------------------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class NewBehaviourScript : MonoBehaviour
{
    //所有文件的路径数组
    public string[] allTexture;
    //文件夹路径
    public string downloadPath;
    // 存储加载的图片预制体
    public List<Button> loadedImages = new List<Button>();

    // 用于加载图片数据的Texture2D对象
    private Texture2D mytex; 

    //预制体和预制体的父级
    private Button btn_Item;
    private Transform parent;

    // Start is called before the first frame update
    void Start()
    {
        //加载所有的图片
        downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + "测试";
        Debug.Log(DateTime.Now + "开始读取");
        // 调用方法开始加载图片
        LoadImagesFromFolder(downloadPath);
    }
    
    void LoadImagesFromFolder(string folderPath)
    {
        // 获取文件夹下所有图片文件路径,并倒序排列
        allTexture = Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly)
            .Where(s => s.EndsWith(".jpg") || s.EndsWith(".png") || s.EndsWith(".jpeg")) // 过滤图片格式
            .OrderByDescending(f => f) // 倒序排列
            .ToArray(); // 转换为数组

        StartCoroutine(LoadLoadImages());

    }
  

    private IEnumerator LoadLoadImages()
    {
        for (int i = 0; i < allTexture.Length; i++)
        {
            yield return CreatePrefab(i);
        }
    }


    async Task CreatePrefab(int i)
    {

        await Task.Run(() => {
            Debug.Log("生成一个");
            // 异步读取图片文件数据
            byte[] fileData = File.ReadAllBytes(allTexture[i]);

            // 在主线程中生成图片预制体
            UnityMainThreadDispatcher.Instance().Enqueue(() =>
            {
                // 创建 Texture2D 对象并加载图片数据
                Texture2D tex = new Texture2D(1, 1);
                tex.LoadImage(fileData);
                Button newImage = Instantiate(btn_Item, parent); // 实例化图片预制体

                newImage.GetComponent<Image>().sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); // 设置图片精灵
                loadedImages.Add(newImage); // 添加到已加载图片集合中

            });

        });


        // 等待下一帧,防止主线程阻塞
        await Task.Yield();
    }
 
}
------------------------------------------------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值