#Unity _ AssetBundle 数据打包
程序员文章站
2022-04-03 15:38:30
...
一、AseetBundle MD5加密
MD5原理 利用进制转换 ,传入文件路径,最后返回MD5加密后的字符串。
-
编写 Common 类,引用三个类
using ng System.IO;
using System.Text;
using System.Security.Cryptography; -
使用文件流,将路径文件打开,将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 工具便捷打包
- 编写 MyEditor 静态类,引用四个类库
using UnityEditor;
using System;
using System.IO;
using System.Text; - 打包 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();
}
- 打包 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();
}
}
- 打包 配置文件 到 服务器 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();
}
}
}
上一篇: python 处理document文档 保留原样式
下一篇: unity 获取泛型脚本的引用