欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

#Unity _ AssetBundle 数据打包

程序员文章站 2022-04-03 15:38:30
...

一、AseetBundle MD5加密

MD5原理 利用进制转换 ,传入文件路径,最后返回MD5加密后的字符串。

  1. 编写 Common 类,引用三个类
    using ng System.IO;
    using System.Text;
    using System.Security.Cryptography;

  2. 使用文件流,将路径文件打开,将MD5加密信息写入文件

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

using System.IO;
using System.Security.Cryptography;
using System.Text;
public class Common 
{
    //传入文件路径 返回md5加密后的字符串
    public static string GetMD5ByFile(string filePath)
    {
        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        {
            MD5 md5 = MD5.Create(); //创建一个MD5
            byte[] retVal = md5.ComputeHash(fs); //md5.ComputeHash需要用字节数组接住
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("X2"));//大写X2,转化为全大写的十六进制
            }
            return sb.ToString();
        }
    }
}

二、AseetBundle Editor 工具便捷打包

  1. 编写 MyEditor 静态类,引用四个类库
    using UnityEditor;
    using System;
    using System.IO;
    using System.Text;
  2. 打包 AssetBundle 到 服务器 文件夹

关于导出路径

Application.streamingAssetsPath
pc或者mac 有读写权限但是在移动端只能读

Application.persistentDataPath
这个路径任何平台都可以有读写权限

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Text;


public static class MyEditor
{
    public static string outPath = "D:/Server/StreamingAssets";   //导出的文件夹路径
    
    [MenuItem("我的工具/打包ab(服务器文件夹)")]
    public static void ExportAssetBundles()
    {
        if (!Directory.Exists(outPath))
        {
            //没有这个文件夹就创建
            Directory.CreateDirectory(outPath);
        }
        //输出路径 压缩模式 打包平台
        BuildPipeline.BuildAssetBundles(outPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
        
        //刷新unity编辑器
        AssetDatabase.Refresh();
    }

  1. 打包 AssetBundle 到 本地StreamingAssets 文件夹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Text;

public static class MyEditor
{
	[MenuItem("我的工具/打包ab(本地StreamingAssets文件夹)")]
    public static void ExportAssetBundlesLocal()
    {
        if (!Directory.Exists(Application.streamingAssetsPath))
        {
            //没有这个文件夹就创建
            Directory.CreateDirectory(Application.streamingAssetsPath);
        }
        //输出路径 压缩模式 打包平台
        BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);

        //刷新unity编辑器
        AssetDatabase.Refresh();
    }
}

  1. 打包 配置文件 到 服务器 Server 文件夹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Text;

public static class MyEditor
{
    public static string outPath = "D:/Server/StreamingAssets";   //导出的文件夹路径

	[MenuItem("我的工具/导出配置文件")]
    public static void ExportConfigData()
    {
        string configPath = "D:/Server/"; //配置文件的导出路径
        string[] filePaths = Directory.GetFiles(outPath);
        //创建一个config.txt的文件
        using (FileStream fs = new FileStream(configPath + "config.txt", FileMode.Create))
        {
            StringBuilder sb = new StringBuilder();//用来拼接字符串的类型

            for (int i = 0; i < filePaths.Length; i++)
            {
                string fileName = Path.GetFileName(filePaths[i]); //文件名
                string md5 = Common.GetMD5ByFile(filePaths[i]); //加密后的md5字符串
                sb.Append(fileName + "\t" + md5);
                if (i != filePaths.Length - 1) //最后一行不换行
                {
                    sb.AppendLine();//换行
                }
            }
            //将字符串写入文件流
            //1.将字符串转换二进制
            byte[] data = Encoding.UTF8.GetBytes(sb.ToString());
            //2.写入文件流
            fs.Write(data, 0, data.Length);
        }
    }

三、单例类 ResourcesManager 资源管理器

任务清单

  • 协调方法,IEnumerator LoadConfigData ( ),用于加载服务器中的配置文件信息
  • 协调方法,IEnumerator DownAssetAb(string fileName) ,用于下载资源
  • 泛型方法,LoadAsset,用于加载资源
  • AssetBundle类型方法,AssetBundle GetAssetBundle(string assetName),用于获取AssetBundle资源,通过ab名称得到对应的ab对象
  • AssetBundle类型方法,AssetBundle LoadAssetBundle(string assetName),用于加载ab包
  • void LoadDependenciesAssetBundle(string assetName),用于加载某个ab文件需要的ab资源
  • public void DeleteAssetBundle(string assetName, bool isUnLoadAllObj = false),用于释放单个ab资源
  • public void DeleteAllAssetBundles(bool isUnLoadAllObj = false),用于 //释放所有的ab资源
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;//网络相关的命名空间
using System.IO;
using System.Linq;
//资源管理器

public class ResourcesManager : MonoBehaviour
{
    public static ResourcesManager Instance;

    private string configDataUrl = "file://D:/Server/config.txt";

    private string serverSteamingAssetUrl = "file://D:/Server/StreamingAssets/";

    private string streamingAssetsPath = Application.streamingAssetsPath + "/";

    public bool isLoad = false;//是否去服务器加载ab资源
    private void Awake()
    {
        Instance = this;
        assetDic = new Dictionary<string, AssetBundle>();
        assetTimeDic = new Dictionary<string, float>();
        if (isLoad == true)
        {
            StartCoroutine(LoadConfigData());
        }
    }
    //加载服务器中的配置文件信息
    private IEnumerator LoadConfigData()
    {
        //请求一个服务器地址里的配置文件
        using (UnityWebRequest request = UnityWebRequest.Get(configDataUrl))
        {
            yield return request.SendWebRequest(); //发送请求

            if (request.isDone == true)
            {
                //返回请求后的结果
                DownloadHandler handle = request.downloadHandler;
                string txt = handle.text;//读取当前得到的文本数据

                //按行的形式进行切割
                string[] arr = txt.Split('\n');

                for (int i = 0; i < arr.Length; i++)
                {
                    //按tab键的形式切割
                    string[] val = arr[i].Trim().Split('\t');
                    string fileName = val[0];//文件名
                    string md5 = val[1];//md5字符串
                    //判断streamingAssetsPath文件夹里面是否有fileName的文件
                    string filePath = streamingAssetsPath + fileName;//文件路径
                    if (!File.Exists(filePath))
                    {
                        //不存在 就要从服务器中下载
                        Debug.Log("不存在");
                        yield return StartCoroutine(DownAssetAb(fileName));
                    }
                    else
                    {
                        Debug.Log("存在");
                        //对本地的当前文件进行加密 
                        string currentMd5 = Common.GetMD5ByFile(filePath);
                        if (currentMd5 == md5)
                        {
                            //不需要更新
                        }
                        else
                        {
                            //文件有变换 需要重新下载
                            Debug.Log("有更新");
                            yield return StartCoroutine(DownAssetAb(fileName));
                        }
                    }
                }
            }
        }
    }


    //下载资源
    private IEnumerator DownAssetAb(string fileName)
    {
        string fileUrl = serverSteamingAssetUrl + fileName;//文件对应服务器路径

        using (UnityWebRequest request = UnityWebRequest.Get(fileUrl))
        {
            yield return request.SendWebRequest(); //发送请求


            if (request.isDone == true)
            {
                //得到文件数据 
                DownloadHandler handle = request.downloadHandler;
                //得到文件的二进制数据
                byte[] bytes = handle.data;

                //生成对应的文件进行存储
                using (FileStream fs = new FileStream(streamingAssetsPath + fileName, FileMode.Create))
                {
                    //写入数据
                    fs.Write(bytes, 0, bytes.Length);
                }
            }
        }
    }


    private Dictionary<string, AssetBundle> assetDic;//存储已经加载到的ab文件
    private Dictionary<string, float> assetTimeDic;//存储ab文件的存在时间

    private float releaseTime = 30;//释放不在使用的资源的时长


    public T LoadAsset<T>(string assetName, string objName) where T : Object
    {
        AssetBundle ab = GetAssetBundle(assetName);
        if (ab != null)
        {
            return ab.LoadAsset<T>(objName);
        }
        return null;
    }

    //通过ab名称得到对应的ab对象
    //参数 assetName 不带后缀
    private AssetBundle GetAssetBundle(string assetName)
    {
        //名字 + 后缀
        assetName = assetName + ".unity3d";

        //把名字转成小写
        assetName = assetName.ToLower();

        //先从存放ab字典中查找
        if (assetDic.ContainsKey(assetName))
        {
            //把生命周期重置
            assetTimeDic[assetName] = 30;
            return assetDic[assetName];
        }
        else
        {
            //assetName 已经加了文件后缀
            return LoadAssetBundle(assetName);
        }
    }

    //加载ab
    private AssetBundle LoadAssetBundle(string assetName)
    {
        string path = streamingAssetsPath + assetName; //拼接ab文件的路径 (已经下载到本地的路径 不是服务器的路径)

        AssetBundle ab = AssetBundle.LoadFromFile(path);

        //加载当前ab资源所需要的ab清单(依赖的ab文件)
        LoadDependenciesAssetBundle(assetName);

        if (ab != null)
        {
            //添加到存储ab的字典当中
            assetDic.Add(assetName, ab);
            //添加生命周期的字典
            assetTimeDic.Add(assetName, 30);
            
            return ab;
        }
        return null;
    }

    //加载某个ab文件需要的ab资源
    private void LoadDependenciesAssetBundle(string assetName)
    {
        //加载总依赖的ab文件 (这个文件的名称跟当前文件的文件夹同名)
        string path = streamingAssetsPath + "StreamingAssets";

        AssetBundle manifestAb = AssetBundle.LoadFromFile(path); //得到的拥有所有依赖信息的ab文件

        AssetBundleManifest manifest = manifestAb.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

        //释放当前依赖的ab文件
        manifestAb.Unload(false);

        string[] arr = manifest.GetAllDependencies(assetName);

        for (int i = 0; i < arr.Length; i++)
        {
            //当前这个ab资源在字典中不存在 就要加载
            if (!assetDic.ContainsKey(arr[i]))
            {
                LoadAssetBundle(arr[i]);
            }
        }
    }

    //释放单个ab资源
    public void DeleteAssetBundle(string assetName, bool isUnLoadAllObj = false)
    {
        assetName = assetName + ".unity3d"; //拼出有后缀的ab名称
        if (assetDic.ContainsKey(assetName))
        {
            assetDic[assetName].Unload(isUnLoadAllObj);
            assetDic.Remove(assetName);
            assetTimeDic.Remove(assetName);
        }
    }

    //清完所有的ab资源
    public void DeleteAllAssetBundles(bool isUnLoadAllObj = false)
    {
        foreach (var item in assetDic)
        {
            item.Value.Unload(isUnLoadAllObj);
        }
        assetDic.Clear();
        assetTimeDic.Clear();
    }
    
    string[] keys;
    private void Update()
    {
        //想用for循环遍历字典中的键 key
        keys = assetDic.Keys.ToArray();
        for (int i = 0; i < keys.Length; i++)
        {
            assetTimeDic[keys[i]] -= Time.deltaTime;
            if (assetTimeDic[keys[i]] <= 0)
            {
                //释放
                DeleteAssetBundle(keys[i]);
            }
        }

        //释放一些已经没有引用的资源
        releaseTime -= Time.deltaTime;
        if (releaseTime <= 0)
        {
            releaseTime = 30;

            Resources.UnloadUnusedAssets();
        }
    }
}

相关标签: Unity技巧