using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
//初始版本
//参考https://blog.csdn.net/damenhanter/article/details/50221841
public class ExportAssetBundles : EditorWindow
{
[MenuItem("Build/ExportResource")]
static void ExportResource()
{
//打包方式一
//打开保存文件面板,获取用户选择的路径
//string path = EditorUtility.SaveFilePanel("Save Resource","" ,"New Resource", "assetbundle");
//Debug.Log(path);//保持AB包路径
//if(path.Length!= 0)
//{
// //选择要保存的对象 选择模式包含文件夹
// Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
// //打包
// BuildPipeline.BuildAssetBundle(Selection.activeObject,selection,path,BuildAssetBundleOptions.CollectDependencies
// |BuildAssetBundleOptions.CompleteAssets,BuildTarget.StandaloneWindows);
// //BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.CollectDependencies
// // | BuildAssetBundleOptions.CompleteAssets, BuildTarget.StandaloneWindows);
//}
//打包方式二 现在常用方式
//string outPath = Application.dataPath + "/Test";
//string resPath = Application.dataPath + "/Prefabs";
//SetAssetBundleName(new DirectoryInfo(resPath));//设置每个资源的ab包名
//Caching.ClearCache();//清除内存ab包缓存
//BuildPipeline.BuildAssetBundles(outPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
//AssetDatabase.Refresh();//刷新unity文件夹
//尝试手动选择 资源路径和ab输出路径
//string path = EditorUtility.OpenFolderPanel("", "", "");
//Debug.Log(path);//得到选中的文件夹
}
//设置ab包名 根据资源所在文件夹
private static void SetAssetBundleName(DirectoryInfo directoryInfo)
{
DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories();//获取当前目录的子目录(不包含孙子目录)
Debug.Log(directoryInfos.Length);
FileInfo[] fileInfos = directoryInfo.GetFiles();//获取目录中的文件,不包含子目录当中的文件 包含.meta文件
Debug.Log(fileInfos.Length);
for (int i = 0; i < directoryInfos.Length; i++)
{
SetAssetBundleName(directoryInfos[i]);
}
for (int i = 0; i < fileInfos.Length; i++)
{
FileInfo info = fileInfos[i];
//根据扩展名来判断
if (info.Extension != ".meta" && info.Extension != ".cs")
{
string path = info.FullName.Replace(@"\", @"/").Replace(Application.dataPath.Replace(@"\", @"/"), "");
path = "Assets" + path;
Debug.Log(path);
//TODO
string assetbundleName = path.Replace("Assets" + "/Prefabs" + "/", "").Replace(info.Extension, "");
AssetImporter assetImporter = AssetImporter.GetAtPath(path);
assetImporter.assetBundleName = assetbundleName;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using System;
using System.Security.Cryptography;
/// <summary>
/// 通过自定义窗口打包ab包,并且压缩成zip 把zip放在StreamingAssets文件夹下,安卓第一次运行时拷贝到Application.persistentDataPath解压
/// 生成版本号
/// 存储abname和MD5值以及文件大小在一个文件中 用于比对
/// </summary>
public class ExportAssetBundles : EditorWindow
{
//脚本无需打包成ab包 .lua .cs .json
[MenuItem("Build/ExportResource")]
static void ExportResource()
{
//使用UnityEditorWindow自建窗口扩展 提示
ExportAssetBundles myWindow = (ExportAssetBundles)EditorWindow.GetWindow<ExportAssetBundles>();
myWindow.minSize = new Vector2(800, 600);
myWindow.Show();
}
static string resPath = "";//需要打包的资源路径 统一放在一个文件夹下
static string outPath = "";//打包的ab包输出路径 统一放在一个文件夹下
static string zipPath = "";//打包的ab包压缩包输出路径
static string version = "";//版本号
static MyZip myZip;
BuildTarget buildtarget = BuildTarget.StandaloneWindows;//设置打包平台
private void OnGUI()
{
GUILayout.BeginVertical();//开始竖直方向排列
GUILayout.Space(5);//竖向间隔
//设置需要打包的资源的路径
GUILayout.BeginHorizontal();
GUI.skin.label.fontSize = 14;
GUILayout.Label("Build Resources Path:");
GUILayout.Space(5);//横向间隔
resPath = EditorGUILayout.TextField(resPath);
GUILayout.Space(5);//横向间隔
if (GUILayout.Button("请选择需要打包的资源路径文件夹"))
{
resPath = EditorUtility.OpenFolderPanel("", "", "");//得到选中的文件夹
}
GUILayout.EndHorizontal();
GUILayout.Space(5);//间隔
//设置已经打包的ab包的输出路径
GUILayout.BeginHorizontal();
GUI.skin.label.fontSize = 14;
GUILayout.Label("AssetBundle Output Path:");
GUILayout.Space(5);//横向间隔
outPath = EditorGUILayout.TextField(outPath);
GUILayout.Space(5);//横向间隔
if (GUILayout.Button("请选择打包输出路径"))
{
outPath = EditorUtility.OpenFolderPanel("", "", "");//得到选中的文件夹
}
GUILayout.EndHorizontal();
//设置压缩ab包zip的输出路径
GUILayout.BeginHorizontal();
GUI.skin.label.fontSize = 14;
GUILayout.Label("AssetBundle Zip Output Path:");
GUILayout.Space(5);//横向间隔
zipPath = EditorGUILayout.TextField(zipPath);
GUILayout.Space(5);//横向间隔
if (GUILayout.Button("请选择压缩ab包的输出路径"))
{
zipPath = EditorUtility.SaveFilePanel("assets", "","assetbundles", "zip");//得到选中的文件夹
}
GUILayout.EndHorizontal();
//选择打包的平台
GUILayout.Space(5);//竖向间隔
GUILayout.BeginHorizontal();
GUILayout.Label("Select Platform:");
buildtarget = (BuildTarget)EditorGUILayout.EnumPopup(buildtarget);
GUILayout.EndHorizontal();
//打包的版本 assetbundle version
//todo
GUILayout.Space(5);//竖向间隔
GUILayout.BeginHorizontal();
GUI.skin.label.fontSize = 14;
GUILayout.Label("AssetBundle Version:");
GUILayout.Space(5);//横向间隔
version = EditorGUILayout.TextField(version);
GUILayout.Space(5);//横向间隔
if (GUILayout.Button("生成版本文件"))
{
if (version == "")
{
//弹出unity提示窗口
EditorUtility.DisplayDialog("提示", "生成失败,请确认已经填写版本号", "理解");
}
else
{
if (resPath == "")
{
EditorUtility.DisplayDialog("提示", "生成失败,请确认已经填写打包资源路径", "理解");
}
else
{
if (!Directory.Exists(Application.streamingAssetsPath))
{
Directory.CreateDirectory(Application.streamingAssetsPath);
}
File.WriteAllText(Path.Combine(Application.streamingAssetsPath, "version.txt"), version);
File.WriteAllText(Path.Combine(resPath, "version.txt"), version);
}
}
}
GUILayout.EndHorizontal();
//开始打包
GUILayout.Space(5);//竖向间隔
if (GUILayout.Button("开始打包AssetBundle"))
{
if(resPath != "" && outPath != ""&&zipPath!="")
{
//开始打包
//此处可以根据文件类型进行分类 暂不分类
SetAssetBundleName(new DirectoryInfo(resPath));//设置每个资源的ab包名
Caching.ClearCache();//清除内存ab包缓存
BuildPipeline.BuildAssetBundles(outPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
AssetDatabase.Refresh();//刷新unity文件夹
Caching.ClearCache();//清除内存ab包缓存
Debug.Log("打包完成");
Debug.Log("需要打包的资源所在目录:" + resPath + " 打包后的AssetBundle所在目录:" + outPath);
}
else
{
//弹出unity提示窗口
EditorUtility.DisplayDialog("提示","打包失败,请确认已经选择路径","理解");
}
}
//打包完生成MD5文件
GUILayout.Space(5);//竖向间隔
if (GUILayout.Button("生成md5的file文件"))
{
if ( outPath != "" )
{
//获取所有ab文件md5 写进文件file.json
Caching.ClearCache();//清除内存ab包缓存
string totalABPath = outPath.Substring(outPath.LastIndexOf("/") + 1);
AssetBundle ab = AssetBundle.LoadFromFile(outPath+"/"+totalABPath);//加载所有AssetBundle
AssetBundleManifest manifest = (AssetBundleManifest)ab.LoadAsset("AssetBundleManifest");//加载对应AssetBundleManifest
ab.Unload(false);
string[] abnames = manifest.GetAllAssetBundles();//获取所有ab包名
for (int i = 0; i < abnames.Length; i++)
{
Debug.Log(abnames[i]);
}
List<AssetDesc> abInfo = new List<AssetDesc>();//存储MD5
abInfo.Add(new AssetDesc(totalABPath, GetFileMd5(outPath + "/" + totalABPath), File.ReadAllBytes(outPath + "/" + totalABPath).Length));
abInfo.Add(new AssetDesc(totalABPath + ".manifest", GetFileMd5(outPath + "/" + totalABPath + ".manifest"), File.ReadAllBytes(outPath + "/" + totalABPath + ".manifest").Length));
foreach (string name in abnames)
{
//没有后缀的文件
if (File.Exists(Path.Combine(outPath, name)))
{
abInfo.Add(new AssetDesc(name, GetFileMd5(Path.Combine(outPath, name)), File.ReadAllBytes(Path.Combine(outPath, name)).Length));
}
else
{
Debug.Log("路径有问题" + Path.Combine(outPath, name));
return;
}
//
if (File.Exists(Path.Combine(outPath, name + ".manifest")))
{
abInfo.Add(new AssetDesc(name + ".manifest", GetFileMd5(Path.Combine(outPath, name + ".manifest")), File.ReadAllBytes(Path.Combine(outPath, name + ".manifest")).Length));
}
else
{
Debug.Log("路径有问题" + Path.Combine(outPath, name + ".manifest"));
return;
}
}
//如何把List集合变成json数据
var result = Newtonsoft.Json.JsonConvert.SerializeObject(abInfo, Newtonsoft.Json.Formatting.Indented);
var resultPath = Path.Combine(outPath, "file.json");
File.WriteAllText(resultPath, result);
if(!Directory.Exists(Application.streamingAssetsPath))
{
Directory.CreateDirectory(Application.streamingAssetsPath);
}
File.WriteAllText(Path.Combine(Application.streamingAssetsPath, "file.json"), result);
AssetDatabase.Refresh();//刷新unity文件夹
}
else
{
//弹出unity提示窗口
EditorUtility.DisplayDialog("提示", "生成失败,请确认已经选择路径", "理解");
}
}
//打包完开始压缩ab包
GUILayout.Space(5);//竖向间隔
if (GUILayout.Button("开始压缩ab包"))
{
//需要有压缩输出路径,以及打包的ab包
if (zipPath != "")
{
//开始压缩
myZip = new MyZip();
myZip.Restart();
myZip.ZipFloder(outPath, zipPath);
Debug.Log("压缩成功");
AssetDatabase.Refresh();//刷新unity文件夹
}
}
GUILayout.EndVertical();//结束竖直方向排列
}
//设置ab包名 根据资源所在文件夹
private static void SetAssetBundleName(DirectoryInfo directoryInfo)
{
DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories();//获取当前目录的子目录(不包含孙子目录)
Debug.Log(directoryInfos.Length);
FileInfo[] fileInfos = directoryInfo.GetFiles();//获取目录中的文件,不包含子目录当中的文件 包含.meta文件
Debug.Log(fileInfos.Length);
for (int i = 0; i < directoryInfos.Length; i++)
{
SetAssetBundleName(directoryInfos[i]);
}
for (int i = 0; i < fileInfos.Length; i++)
{
FileInfo info = fileInfos[i];
//根据扩展名来判断
if (info.Extension != ".meta" && info.Extension != ".cs"&& info.Extension != ".lua")
{
string path = info.FullName.Replace(@"\", @"/").Replace(Application.dataPath.Replace(@"\", @"/"), "");
path = "Assets" + path;
Debug.Log(path);
TODO
string temp = resPath.Substring(resPath.LastIndexOf("/")+1);
Debug.Log(temp);
string assetbundleName = path.Replace("Assets/"+ temp + "/", "").Replace(info.Extension, "");
Debug.Log(assetbundleName);
//string assetbundleName = path.Replace("Assets/Prefabs" + "/", "").Replace(info.Extension, "");
AssetImporter assetImporter = AssetImporter.GetAtPath(path);
assetImporter.assetBundleName = assetbundleName;
}
}
}
//清除ab包名
public static void ClearAssetBundleName(DirectoryInfo directoryInfo)
{
DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories();
FileInfo[] fileInfos = directoryInfo.GetFiles();
for (int i = 0; i < directoryInfos.Length; i++)
{
ClearAssetBundleName(directoryInfos[i]);
}
for (int i = 0; i < fileInfos.Length; i++)
{
if (!fileInfos[i].Extension.Contains(".meta") && !fileInfos[i].Extension.Contains(".cs"))
{
string path = fileInfos[i].FullName.Replace(@"\", @"/").Replace(Application.dataPath.Replace(@"\", @"/"), "");
path = "Assets" + path;
AssetImporter assetImporter = AssetImporter.GetAtPath(path);
assetImporter.assetBundleName = string.Empty;
}
}
}
//获取文件MD5值
public static string GetFileMd5(string filename)
{
//string filename = Application.dataPath+"/Test/CompressZip.zip";
string filemd5 = null;
try
{
using (var fileStream = File.OpenRead(filename))
{
var md5 = MD5.Create();
var fileMD5Bytes = md5.ComputeHash(fileStream);//计算指定Stream 对象的哈希值
filemd5 = FormatMD5(fileMD5Bytes);
}
}
catch (System.Exception ex)
{
Debug.Log(ex);
}
return filemd5;
}
static string FormatMD5(Byte[] data)
{
return System.BitConverter.ToString(data).Replace("-", "").ToLower();//将byte[]装换成字符串
}
}
public class AssetDesc
{
public AssetDesc(string abname, string md5, int size)
{
this.abname = abname;
this.md5 = md5;
this.size = size;
}
public string abname;
public string md5;
public int size;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
using System.Threading;
using ICSharpCode.SharpZipLib.Core;
using System;
//https://blog.csdn.net/hiramtan/article/details/49902359
//unity如何解压压缩包,压缩文件夹
//添加递归方式压缩/解压缩文件夹
//重要
//1.文件/文件夹不支持中文
//2.会阻塞主线程
public class MyZip : MonoBehaviour
{
void Start()
{
//测试压缩
//ZipFloder(Application.dataPath+"/CompressZip", Application.dataPath + "/Test/CompressZip.zip");
//测试解压
UnZipFile(Application.dataPath + "/Test/CompressZip.zip", Application.dataPath + "/Scenes");
}
//进度刷新时间
public float progressUpdateTime = 0.2f;
//每个文件的压缩/解压进度
public float progress { private set; get; }
//文件的压缩/解压总进度
public float progressOverall { private set; get; }
public void Restart()
{
if(thread!=null)
thread.Abort();
thread = null;
}
public void StopThread()
{
thread.Abort();
}
Thread thread;
/// <summary>
/// 压缩文件夹 应当在编辑器模式下执行
/// </summary>
/// <param name="_fileFolder">文件夹路径</param>
/// <param name="_outZip">zip文件路径+名字</param>
public void ZipFloder(string _fileFolder, string _outZip)
{
string directory = _outZip.Substring(0, _outZip.LastIndexOf("/"));//测一下
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
if (File.Exists(_outZip))
File.Delete(_outZip);
progress = progressOverall = 0;
thread = new Thread(delegate ()
{
int fileCount = FileCount(_fileFolder);//文件总数
int fileCompleted = 0;
FastZipEvents events = new FastZipEvents();
events.Progress = new ProgressHandler((object sender, ProgressEventArgs e) =>
{
progress = e.PercentComplete;
if (progress == 100)
{
fileCompleted++;
progressOverall = 100 * fileCompleted / fileCount;
}
});
events.ProgressInterval = TimeSpan.FromSeconds(progressUpdateTime);
events.ProcessFile = new ProcessFileHandler( (object sender, ScanEventArgs e) => { });
//压缩
FastZip fastZip = new FastZip(events);
fastZip.CreateZip(_outZip, _fileFolder, true, "");
});
thread.IsBackground = true;
thread.Start();
}
/// <summary>
/// 解压zip文件 运行时执行
/// </summary>
/// <param name="_zipFIle">需要解压的zip路径和名字</param>
/// <param name="_outFolder">解压路径</param>
public void UnZipFile(string _zipFile,string _outFolder)
{
//如果解压之前存在同名文件,解压之后会直接替换旧文件
if (!Directory.Exists(_outFolder))
Directory.CreateDirectory(_outFolder);
progress = progressOverall = 0;
thread = new Thread(delegate ()
{
int fileCount = (int)new ZipFile(_zipFile).Count;
int fileCompleted = 0;
FastZipEvents events = new FastZipEvents();
events.Progress = new ProgressHandler((object sender, ProgressEventArgs e) =>
{
progress = e.PercentComplete;
if (progress == 100)
{
fileCompleted++;
progressOverall = 100 * fileCompleted / fileCount;
}
});
events.ProgressInterval = TimeSpan.FromSeconds(progressUpdateTime);
events.ProcessFile = new ProcessFileHandler((object sender, ScanEventArgs e) => { });
//解压
FastZip fastZip = new FastZip(events);
fastZip.ExtractZip(_zipFile, _outFolder, "");
});
thread.IsBackground = true;
thread.Start();
}
//https://github.com/hiramtan/HiZip_unity
private int FileCount(string path)
{
int result = Directory.GetFiles(path).Length;
string[] subFolders = Directory.GetDirectories(path);
foreach (string subFolder in subFolders)
{
result += FileCount(subFolder);
}
return result;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
//如何从服务器下载资源
public class DownLoadFile : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine(SaveFile(@"D:\Test\New\version.txt"));
StartCoroutine(SaveFile(@"D:\Test\New\MainBGM.mp3"));
StartCoroutine(SaveFile(@"D:\Test\New\New.rar"));
StartCoroutine(SaveFile(@"D:\Test\New\1.rar"));
StartCoroutine(SaveFile(@"D:\Test\New\11.rar"));
StartCoroutine(SaveFile(@"D:\Test\New\111.rar"));
StartCoroutine(SaveFile(@"D:\Test\New\1111.rar"));
}
public float LoadProcess;
IEnumerator SaveFile(string path)
{
while (!Caching.ready)
{
yield return null;
}
using (UnityWebRequest uwr=UnityWebRequest.Get(path))
{
yield return uwr.SendWebRequest();
if (uwr.error!=null)
{
throw new Exception("www download had an error" + uwr.error);
}
LoadProcess = uwr.downloadProgress;
print(LoadProcess);
if (uwr.isDone)
{
byte[] results = uwr.downloadHandler.data;
FileInfo fileInfo = new FileInfo(Application.dataPath + "/" + path.Substring(path.LastIndexOf(@"\")+1));
FileStream fs = fileInfo.Create();
//fs.Write(字节数组, 开始位置, 数据长度);
fs.Write(uwr.downloadHandler.data, 0, uwr.downloadHandler.data.Length);
fs.Flush(); //文件写入存储到硬盘
fs.Close(); //关闭文件流对象
fs.Dispose(); //销毁文件对象
}
}
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
//接下来结合lua实现热更新效果
public class Test : MonoBehaviour
{
MyZip myZip;
bool isLoadScene = true;
// Start is called before the first frame update
void Start()
{
//解压资源
//myZip = new MyZip();
//myZip.UnZipFile(Application.dataPath + "/Scenes/assetbundles.zip", Application.dataPath + "/Scenet");
//从服务器下载资源到本地
}
// Update is called once per frame
void Update()
{
//unity打包ab包到更新整个流程
//第一步判断Application.persistentDataPath里面有没有对应ab包文件
//如果没有就是第一次打开应用才会如此第一步拷贝Application.streamingAssetsPath里面的ab包压缩包到目录Application.persistentDataPath,然后解压
//如果有就不需要解压
//从服务器下载版本号和file.json文件MD5
//先判断版本号是否一致
//若不一致 下载服务器ab包压缩包 下载file.json文件转换为对应list
//判断文件的MD5是否有变化 用list存储md5有变化的ab包 得到需要更新的资源列表
//根据更新资源列表下载资源 问题下载了同名文件会怎么处理
//下载完毕,更换版本和file.json文件
//开始加载执行lua代码
//如果一致 就没事直接加载lua代码
判断是否解压完毕从服务器下载的资源
if (myZip.progressOverall>=100&& isLoadScene)
{
myZip.StopThread();
//Debug.Log(File.ReadAllText(Application.dataPath + "/Scenet/file.json"));
isLoadScene = false;
//因为已经下载好了,只需要从本地ab包加载资源就可以了
AssetBundle ab = AssetBundle.LoadFromFile(Application.dataPath + "/Scenet/Cube");
GameObject go= ab.LoadAsset<GameObject>("Cube");
GameObject cube = Instantiate<GameObject>(go);
cube.transform.localPosition = Vector3.zero;
//StartCoroutine(LoadWWW(Application.dataPath + "/Scenet/Cube"));
}
}
//从ab包加载资源
private WWW Loadab;
IEnumerator LoadWWW(string path)
{
Loadab = new WWW(path);
yield return Loadab;
if (Loadab.error!=null)
{
Debug.Log(Loadab.error);
}
else
{
GameObject go = Loadab.assetBundle.LoadAsset<GameObject>("Cube");
GameObject cube = Instantiate<GameObject>(go);
cube.transform.localPosition = Vector3.zero;
}
}
}