Unity 文件压缩与线程使用

一、准备工作

导入ICSharpCode.SharpZipLib.dll。
下载地址:https://download.csdn.net/download/yasinxin/11861899

二、zip压缩代码

参考地址:https://www.cnblogs.com/kanekiken/p/8033685.html

using System.IO;
using System.Collections;
using UnityEngine;
using ICSharpCode.SharpZipLib.Zip;

namespace Ardez
{
    
    
    public static class ZipUtility
    {
    
    
        #region ZipCallback
        public abstract class ZipCallback
        {
    
    
            /// <summary>
            /// 压缩单个文件或文件夹前执行的回调
            /// </summary>
            /// <param name="_entry"></param>
            /// <returns>如果返回true,则压缩文件或文件夹,反之则不压缩文件或文件夹</returns>
            public virtual bool OnPreZip(ZipEntry _entry)
            {
    
    
                return true;
            }

            /// <summary>
            /// 压缩单个文件或文件夹后执行的回调
            /// </summary>
            /// <param name="_entry"></param>
            public virtual void OnPostZip(ZipEntry _entry) {
    
     }

            /// <summary>
            /// 压缩执行完毕后的回调
            /// </summary>
            /// <param name="_result">true表示压缩成功,false表示压缩失败</param>
            public virtual void OnFinished(bool _result) {
    
     }
        }
        #endregion

        #region UnzipCallback
        public abstract class UnzipCallback
        {
    
    
            /// <summary>
            /// 解压单个文件或文件夹前执行的回调
            /// </summary>
            /// <param name="_entry"></param>
            /// <returns>如果返回true,则压缩文件或文件夹,反之则不压缩文件或文件夹</returns>
            public virtual bool OnPreUnzip(ZipEntry _entry)
            {
    
    
                return true;
            }

            /// <summary>
            /// 解压单个文件或文件夹后执行的回调
            /// </summary>
            /// <param name="_entry"></param>
            public virtual void OnPostUnzip(ZipEntry _entry) {
    
     }

            /// <summary>
            /// 解压执行完毕后的回调
            /// </summary>
            /// <param name="_result">true表示解压成功,false表示解压失败</param>
            public virtual void OnFinished(bool _result) {
    
     }
        }
        #endregion

        /// <summary>
        /// 压缩文件和文件夹
        /// </summary>
        /// <param name="_fileOrDirectoryArray">文件夹路径和文件名</param>
        /// <param name="_outputPathName">压缩后的输出路径文件名</param>
        /// <param name="_password">压缩密码</param>
        /// <param name="_zipCallback">ZipCallback对象,负责回调</param>
        /// <returns></returns>
        public static bool Zip(string[] _fileOrDirectoryArray, string _outputPathName, string _password = null, ZipCallback _zipCallback = null)
        {
    
    
            if ((null == _fileOrDirectoryArray) || string.IsNullOrEmpty(_outputPathName))
            {
    
    
                if (null != _zipCallback)
                    _zipCallback.OnFinished(false);

                return false;
            }

            ZipOutputStream zipOutputStream = new ZipOutputStream(File.Create(_outputPathName));
            zipOutputStream.SetLevel(6);    // 压缩质量和压缩速度的平衡点
            if (!string.IsNullOrEmpty(_password))
                zipOutputStream.Password = _password;

            for (int index = 0; index < _fileOrDirectoryArray.Length; ++index)
            {
    
    
                bool result = false;
                string fileOrDirectory = _fileOrDirectoryArray[index];
                if (Directory.Exists(fileOrDirectory))
                    result = ZipDirectory(fileOrDirectory, string.Empty, zipOutputStream, _zipCallback);
                else if (File.Exists(fileOrDirectory))
                    result = ZipFile(fileOrDirectory, string.Empty, zipOutputStream, _zipCallback);

                if (!result)
                {
    
    
                    if (null != _zipCallback)
                        _zipCallback.OnFinished(false);

                    return false;
                }
            }

            zipOutputStream.Finish();
            zipOutputStream.Close();

            if (null != _zipCallback)
                _zipCallback.OnFinished(true);

            return true;
        }

        /// <summary>
        /// 解压Zip包
        /// </summary>
        /// <param name="_filePathName">Zip包的文件路径名</param>
        /// <param name="_outputPath">解压输出路径</param>
        /// <param name="_password">解压密码</param>
        /// <param name="_unzipCallback">UnzipCallback对象,负责回调</param>
        /// <returns></returns>
        public static bool UnzipFile(string _filePathName, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
        {
    
    
            if (string.IsNullOrEmpty(_filePathName) || string.IsNullOrEmpty(_outputPath))
            {
    
    
                if (null != _unzipCallback)
                    _unzipCallback.OnFinished(false);

                return false;
            }

            try
            {
    
    
                return UnzipFile(File.OpenRead(_filePathName), _outputPath, _password, _unzipCallback);
            }
            catch (System.Exception _e)
            {
    
    
                Debug.LogError("[ZipUtility.UnzipFile]: " + _e.ToString());

                if (null != _unzipCallback)
                    _unzipCallback.OnFinished(false);

                return false;
            }
        }

        /// <summary>
        /// 解压Zip包
        /// </summary>
        /// <param name="_fileBytes">Zip包字节数组</param>
        /// <param name="_outputPath">解压输出路径</param>
        /// <param name="_password">解压密码</param>
        /// <param name="_unzipCallback">UnzipCallback对象,负责回调</param>
        /// <returns></returns>
        public static bool UnzipFile(byte[] _fileBytes, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
        {
    
    
            if ((null == _fileBytes) || string.IsNullOrEmpty(_outputPath))
            {
    
    
                if (null != _unzipCallback)
                    _unzipCallback.OnFinished(false);

                return false;
            }

            bool result = UnzipFile(new MemoryStream(_fileBytes), _outputPath, _password, _unzipCallback);
            if (!result)
            {
    
    
                if (null != _unzipCallback)
                    _unzipCallback.OnFinished(false);
            }

            return result;
        }

        /// <summary>
        /// 解压Zip包
        /// </summary>
        /// <param name="_inputStream">Zip包输入流</param>
        /// <param name="_outputPath">解压输出路径</param>
        /// <param name="_password">解压密码</param>
        /// <param name="_unzipCallback">UnzipCallback对象,负责回调</param>
        /// <returns></returns>
        public static bool UnzipFile(Stream _inputStream, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
        {
    
    
            if ((null == _inputStream) || string.IsNullOrEmpty(_outputPath))
            {
    
    
                if (null != _unzipCallback)
                    _unzipCallback.OnFinished(false);

                return false;
            }

            // 创建文件目录
            if (!Directory.Exists(_outputPath))
                Directory.CreateDirectory(_outputPath);

            // 解压Zip包
            ZipEntry entry = null;
            using (ZipInputStream zipInputStream = new ZipInputStream(_inputStream))
            {
    
    
                if (!string.IsNullOrEmpty(_password))
                    zipInputStream.Password = _password;

                while (null != (entry = zipInputStream.GetNextEntry()))
                {
    
    
                    if (string.IsNullOrEmpty(entry.Name))
                        continue;

                    if ((null != _unzipCallback) && !_unzipCallback.OnPreUnzip(entry))
                        continue;   // 过滤

                    string filePathName = Path.Combine(_outputPath, entry.Name);

                    // 创建文件目录
                    if (entry.IsDirectory)
                    {
    
    
                        Directory.CreateDirectory(filePathName);
                        continue;
                    }

                    // 写入文件
                    try
                    {
    
    
                        using (FileStream fileStream = File.Create(filePathName))
                        {
    
    
                            byte[] bytes = new byte[1024];
                            while (true)
                            {
    
    
                                int count = zipInputStream.Read(bytes, 0, bytes.Length);
                                if (count > 0)
                                    fileStream.Write(bytes, 0, count);
                                else
                                {
    
    
                                    if (null != _unzipCallback)
                                        _unzipCallback.OnPostUnzip(entry);

                                    break;
                                }
                            }
                        }
                    }
                    catch (System.Exception _e)
                    {
    
    
                        Debug.LogError("[ZipUtility.UnzipFile]: " + _e.ToString());

                        if (null != _unzipCallback)
                            _unzipCallback.OnFinished(false);

                        return false;
                    }
                }
            }

            if (null != _unzipCallback)
                _unzipCallback.OnFinished(true);

            return true;
        }

        /// <summary>
        /// 压缩文件
        /// </summary>
        /// <param name="_filePathName">文件路径名</param>
        /// <param name="_parentRelPath">要压缩的文件的父相对文件夹</param>
        /// <param name="_zipOutputStream">压缩输出流</param>
        /// <param name="_zipCallback">ZipCallback对象,负责回调</param>
        /// <returns></returns>
        private static bool ZipFile(string _filePathName, string _parentRelPath, ZipOutputStream _zipOutputStream, ZipCallback _zipCallback = null)
        {
    
    
            //Crc32 crc32 = new Crc32();
            ZipEntry entry = null;
            FileStream fileStream = null;
            try
            {
    
    
                string entryName = _parentRelPath + '/' + Path.GetFileName(_filePathName);
                entry = new ZipEntry(entryName);
                entry.DateTime = System.DateTime.Now;

                if ((null != _zipCallback) && !_zipCallback.OnPreZip(entry))
                    return true;    // 过滤

                fileStream = File.OpenRead(_filePathName);
                byte[] buffer = new byte[fileStream.Length];
                fileStream.Read(buffer, 0, buffer.Length);
                fileStream.Close();

                entry.Size = buffer.Length;

                //crc32.Reset();
                //crc32.Update(buffer);
                //entry.Crc = crc32.Value;

                _zipOutputStream.PutNextEntry(entry);
                _zipOutputStream.Write(buffer, 0, buffer.Length);
            }
            catch (System.Exception _e)
            {
    
    
                Debug.LogError("[ZipUtility.ZipFile]: " + _e.ToString());
                return false;
            }
            finally
            {
    
    
                if (null != fileStream)
                {
    
    
                    fileStream.Close();
                    fileStream.Dispose();
                }
            }

            if (null != _zipCallback)
                _zipCallback.OnPostZip(entry);

            return true;
        }

        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="_path">要压缩的文件夹</param>
        /// <param name="_parentRelPath">要压缩的文件夹的父相对文件夹</param>
        /// <param name="_zipOutputStream">压缩输出流</param>
        /// <param name="_zipCallback">ZipCallback对象,负责回调</param>
        /// <returns></returns>
        private static bool ZipDirectory(string _path, string _parentRelPath, ZipOutputStream _zipOutputStream, ZipCallback _zipCallback = null)
        {
    
    
            ZipEntry entry = null;
            try
            {
    
    
                string entryName = Path.Combine(_parentRelPath, Path.GetFileName(_path) + '/');
                entry = new ZipEntry(entryName);
                entry.DateTime = System.DateTime.Now;
                entry.Size = 0;

                if ((null != _zipCallback) && !_zipCallback.OnPreZip(entry))
                    return true;    // 过滤

                _zipOutputStream.PutNextEntry(entry);
                _zipOutputStream.Flush();

                string[] files = Directory.GetFiles(_path);
                for (int index = 0; index < files.Length; ++index)
                    ZipFile(files[index], Path.Combine(_parentRelPath, Path.GetFileName(_path)), _zipOutputStream, _zipCallback);
            }
            catch (System.Exception _e)
            {
    
    
                Debug.LogError("[ZipUtility.ZipDirectory]: " + _e.ToString());
                return false;
            }

            string[] directories = Directory.GetDirectories(_path);
            for (int index = 0; index < directories.Length; ++index)
            {
    
    
                if (!ZipDirectory(directories[index], Path.Combine(_parentRelPath, Path.GetFileName(_path)), _zipOutputStream, _zipCallback))
                    return false;
            }

            if (null != _zipCallback)
                _zipCallback.OnPostZip(entry);

            return true;
        }
    }
}

三、压缩运用

些了一个操作类ZipManager.cs来处理压缩与加压逻辑

public static class ZipManager
{
    
    
    /// <summary>
    /// 加密密码
    /// </summary>
    public static readonly string passedWord = "xyh";

    /// <summary>
    /// 压缩
    /// </summary>
    /// <param name="fileAsset">要解压缩的文件路径</param>
    /// <param name="outPutPath">压缩输出全路径(包括后缀名)</param>
    public static void ZipFile(string[] fileAsset, string outPutPath)
    {
    
    
        ZipUtility.Zip(fileAsset, outPutPath, passedWord);
    }

    /// <summary>
    /// 解压
    /// </summary>
    /// <param name="filepath">要解压的文件包</param>
    /// <param name="outPutPath">输出路径</param>
    public static void UnzipFile(string filepath, string outPutPath)
    {
    
    
        ZipUtility.UnzipFile(filepath, outPutPath, passedWord);
    }
}

操作测试代码

public class ZipTest : MonoBehaviour
{
    
    
    string[] _fileOrDirectoryArray = new string[] {
    
     @"D:\ZipTest\测试文件" };
    /// <summary>
    /// 输出路径
    /// </summary>
    string outputPath = @"D:\ZipTest\Output";
    /// <summary>
    /// 压缩路径
    /// </summary>
    string zipPath = @"D:\ZipTest\我的文件.xyh";

    public void OnZipClick()
    {
    
    
        ZipManager.ZipFile(_fileOrDirectoryArray, zipPath);
    }

    public void OnUnZipClick()
    {
    
    
        ZipManager.UnzipFile(zipPath, outputPath);
    }
}

这样压缩与解压基本操作就是实现了。

四、线程压缩

但是如上这样压缩与解压时整个程序会卡主,直到压缩与解压完成。
最开始我想到的是用协程,但发现仍然会卡主。
其实协程实在主线程闲置时运行的,线程才是独立运行的。所以这里就需要使用到线程来进行压缩与解压的操作。
改造 ZipManager 类:

using System.Threading;

public static class ZipManager
{
    
    
    /// <summary>
    /// 加密密码
    /// </summary>
    public static readonly string passedWord = "xyh";

    /// <summary>
    /// 压缩
    /// </summary>
    /// <param name="fileAsset">要解压缩的文件路径</param>
    /// <param name="outPutPath">压缩输出全路径(包括后缀名)</param>
    public static void ZipFile(string[] fileAsset, string outPutPath)
    {
    
    
        ThreadPool.QueueUserWorkItem((p) =>
        {
    
    
            ZipUtility.Zip(fileAsset, outPutPath, passedWord);
        });
    }

    /// <summary>
    /// 解压
    /// </summary>
    /// <param name="filepath">要解压的文件包</param>
    /// <param name="outPutPath">输出路径</param>
    public static void UnzipFile(string filepath, string outPutPath)
    {
    
    
        ThreadPool.QueueUserWorkItem((p) =>
        {
    
    
            ZipUtility.UnzipFile(filepath, outPutPath, passedWord);
        });
    }
}

如上使用了一个线程池,整个进度就丝滑起来了。

五、获取压缩进度

但是新问题又来了,虽然在压缩与解压时不会影响到主线程,可我不知道什么时候做完了操作。
我必须要获取到压缩与解压时的进度。
在 ZipUtility 类中有两个回调委托,ZipCallback 和 UnzipCallback 可以获得压缩与解压的开始、结束和解压好的文件。
为了能够时候计算到进度值,我对这两个回调类进行的扩展:

using ICSharpCode.SharpZipLib.Zip;
using System;
using UnityEngine;
using static ZipUtility;

public class ZipCallbackEvent : ZipCallback
{
    
    
    public Action<long> OnZip;
    public Action<bool> OnZipFinish;
    public long zipSize;

    public override bool OnPreZip(ZipEntry _entry)
    {
    
    
        base.OnPostZip(_entry);
        return true;
    }

    public override void OnPostZip(ZipEntry _entry)
    {
    
    
        base.OnPostZip(_entry);
        OnZip?.Invoke(_entry.Size);
    }

    public override void OnFinished(bool _result)
    {
    
    
        base.OnFinished(_result);
        OnZipFinish?.Invoke(_result);
    }
}
using ICSharpCode.SharpZipLib.Zip;
using System;
using UnityEngine;
using static ZipUtility;

public class UnZipCallbackEvent : UnzipCallback
{
    
    
    public Action<long> OnUnZip;
    public Action<bool> OnUnZipFinish;
    public long unZipSize;

    public override void OnPostUnzip(ZipEntry _entry)
    {
    
    
        base.OnPostUnzip(_entry);
        OnUnZip?.Invoke(_entry.Size);
    }
    public override void OnFinished(bool _result)
    {
    
    
        base.OnFinished(_result);
        OnUnZipFinish?.Invoke(_result);
    }
}

这样就能实时获取到解压和压缩好的文件大小。
这时还需要获取整个文件的大小,如下写了一个工具类ResUtility:

using UnityEngine;
using System;
using System.IO;

public static class ResUtility
{
    
    
    /// <summary>
    /// 删除文件夹内资源
    /// </summary>
    /// <param name="filePath"></param>
    public static void ClearFile(string filePath)
    {
    
    
        if (Directory.Exists(filePath))
        {
    
    
            try
            {
    
    
                var dir = new DirectoryInfo(filePath);
                dir.Attributes = dir.Attributes & ~FileAttributes.ReadOnly;
                dir.Delete(true);
            }
            catch (Exception ex)
            {
    
    
                Debug.LogError(string.Format(" 文件夹存在 删除文件夹时 出现错误 【{0}】", ex.Message));
            }
        }
    }

    /// <summary>
    /// 计算文件或文件夹大小
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static long CalculateSize(string path)
    {
    
    
        if (Directory.Exists(path))
        {
    
    
            return Calculate(path);
        }
        else if (File.Exists(path))
        {
    
    
            return File.ReadAllBytes(path).Length;
        }
        else
        {
    
    
            return 0;
        }
    }

    private static long Calculate(string path)
    {
    
    
        long size = 0;
        //如果是文件,直接计算大小
        string[] files = Directory.GetFiles(path);
        for (int index = 0; index < files.Length; ++index)
        {
    
    
            size += File.ReadAllBytes(files[index]).Length;
        }

        //如果是文件夹,再继续遍历
        string[] directories = Directory.GetDirectories(path);
        for (int index = 0; index < directories.Length; ++index)
        {
    
    
            size += Calculate(directories[index]);
        }

        return size;
    }
}

接下来就是改造 ZipManager 类了

using System;
using System.IO;
using System.Threading;
using UnityEngine;

public static class ZipManager
{
    
    
    /// <summary>
    /// 加密密码
    /// </summary>
    public static readonly string passedWord = "xyh";

    public static ZipCallbackEvent zipCallback = new ZipCallbackEvent();
    public static UnZipCallbackEvent unZipCallback = new UnZipCallbackEvent();

    /// <summary>
    /// 压缩
    /// </summary>
    /// <param name="fileAsset">要解压缩的文件路径</param>
    /// <param name="outPutPath">压缩输出全路径(包括后缀名)</param>
    public static void ZipFile(string[] fileAsset, string outPutPath, Action action = null)
    {
    
    
        ThreadPool.QueueUserWorkItem((p) =>
        {
    
    
            long fileSize = ResUtility.CalculateSize(fileAsset[0]);
            long totalZipSize = 0;
            zipCallback.OnZip = (zipSize) => {
    
    
                totalZipSize += zipSize;
                float progress = totalZipSize * 1.0f / fileSize;
                Debug.Log(zipSize + "   " + fileSize + "   压缩进度:" + progress);
            };
            zipCallback.OnZipFinish = (result) =>
            {
    
    
                if (result)
                {
    
    
                    Debug.Log("压缩成功!");
                    action?.Invoke();
                }
                else
                {
    
    
                    Debug.Log("压缩失败!");
                }
            };

            ZipUtility.Zip(fileAsset, outPutPath, passedWord, zipCallback);
        });
    }

    /// <summary>
    /// 解压
    /// </summary>
    /// <param name="filepath">要解压的文件包</param>
    /// <param name="outPutPath">输出路径</param>
    public static void UnzipFile(string filepath, string outPutPath, Action action = null)
    {
    
    
        ThreadPool.QueueUserWorkItem((p) =>
        {
    
    
            if (Directory.Exists(outPutPath))
            {
    
    
                ResUtility.ClearFile(outPutPath);
            }
            Directory.CreateDirectory(outPutPath);

            long fileSize = ResUtility.CalculateSize(filepath); //获取文件总大小
            long totalUnZipSize = 0;    //已解压的文件大小
            unZipCallback.OnUnZip = (unZipSize) => {
    
    
                totalUnZipSize += unZipSize;
                float progress = totalUnZipSize * 1.0f / fileSize;
                Debug.Log(unZipSize + "   " + fileSize + "   解压进度:" + progress);
            };
            unZipCallback.OnUnZipFinish = (result) =>
            {
    
    
                if (result)
                {
    
    
                    Debug.Log("解压成功!");
                    action?.Invoke();
                }
                else
                {
    
    
                    Debug.Log("解压失败!");
                }
            };
            ZipUtility.UnzipFile(filepath, outPutPath, passedWord, unZipCallback);
        });
    }
}

这样在获得进度的同时,用Action委托来执行压缩解压后的操作。

六、线程中访问Unity组件

在实际开发中遇到个问题,就是Action委托后的的方法中有可能操作Unity的组件,但这个委托又是在线程中的,所以无法访问Unity组件。
这时就需要把委托传递到其他类上执行,如下该着Action的调用:

               Loom.QueueOnMainThread((param) =>
                        {
    
    
                            action?.Invoke();
                        }, null);

Loom ## 标题参考:https://zhuanlan.zhihu.com/p/107698641

/// <summary>
/// Loom 线程内访问Unity组件
/// </summary>
/// <remarks>
/// 2021.6.03: 创建. 辛羊华 <br/>
/// </remarks>
using UnityEngine;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;

namespace Ardez
{
    
    
    public class Loom : UnitySingleton<Loom>
    {
    
    
        //是否已经初始化
        static bool isInitialized;

        private static Loom _ins;
        public static Loom ins {
    
     get {
    
     Initialize(); return _ins; } }

        public override void Awake()
        {
    
    
            base.Awake();
            _ins = this;
            isInitialized = true;
        }

        //初始化
        public static void Initialize()
        {
    
    
            if (!isInitialized)
            {
    
    
                if (!Application.isPlaying)
                    return;

                isInitialized = true;
                var obj = new GameObject("Loom");
                _ins = obj.AddComponent<Loom>();

                DontDestroyOnLoad(obj);
            }
        }

        //单个执行单元(无延迟)
        struct NoDelayedQueueItem
        {
    
    
            public Action<object> action;
            public object param;
        }
        //全部执行列表(无延迟)
        List<NoDelayedQueueItem> listNoDelayActions = new List<NoDelayedQueueItem>();


        //单个执行单元(有延迟)
        struct DelayedQueueItem
        {
    
    
            public Action<object> action;
            public object param;
            public float time;
        }
        //全部执行列表(有延迟)
        List<DelayedQueueItem> listDelayedActions = new List<DelayedQueueItem>();


        //加入到主线程执行队列(无延迟)
        public static void QueueOnMainThread(Action<object> taction, object param)
        {
    
    
            QueueOnMainThread(taction, param, 0f);
        }

        //加入到主线程执行队列(有延迟)
        public static void QueueOnMainThread(Action<object> action, object param, float time)
        {
    
    
            if (time != 0)
            {
    
    
                lock (ins.listDelayedActions)
                {
    
    
                    ins.listDelayedActions.Add(new DelayedQueueItem {
    
     time = Time.time + time, action = action, param = param });
                }
            }
            else
            {
    
    
                lock (ins.listNoDelayActions)
                {
    
    
                    ins.listNoDelayActions.Add(new NoDelayedQueueItem {
    
     action = action, param = param });
                }
            }
        }


        //当前执行的无延时函数链
        List<NoDelayedQueueItem> currentActions = new List<NoDelayedQueueItem>();
        //当前执行的有延时函数链
        List<DelayedQueueItem> currentDelayed = new List<DelayedQueueItem>();

        void Update()
        {
    
    
            if (listNoDelayActions.Count > 0)
            {
    
    
                lock (listNoDelayActions)
                {
    
    
                    currentActions.Clear();
                    currentActions.AddRange(listNoDelayActions);
                    listNoDelayActions.Clear();
                }
                for (int i = 0; i < currentActions.Count; i++)
                {
    
    
                    currentActions[i].action(currentActions[i].param);
                }
            }

            if (listDelayedActions.Count > 0)
            {
    
    
                lock (listDelayedActions)
                {
    
    
                    currentDelayed.Clear();
                    currentDelayed.AddRange(listDelayedActions.Where(d => Time.time >= d.time));
                    for (int i = 0; i < currentDelayed.Count; i++)
                    {
    
    
                        listDelayedActions.Remove(currentDelayed[i]);
                    }
                }

                for (int i = 0; i < currentDelayed.Count; i++)
                {
    
    
                    currentDelayed[i].action(currentDelayed[i].param);
                }
            }
        }

        void OnDisable()
        {
    
    
            if (_ins == this)
            {
    
    
                _ins = null;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/YasinXin/article/details/117534344