/*
* 1. 菜单命令一键打包
* 2. 打包输出目录:StreamAssets/平台/BundleRes400/...
* 3. 依赖保存文件 输出目录/zip/Files.assetBundle.zip
* 4. 打包采用不压缩方式,自己用lzma压缩解压
*/
// 菜单命令 BuildAll 打包 Resources 目录下的所有资源
// 菜单命令 BuildPath 打包 选择目录下 Resources 目录下的所有资源
// 菜单命令 BuildSelFile 打包选择的资源
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Collections;
using System.IO;
using System.Collections.Generic;
using LitJson;
using System.Text;
public class BundleHelper400 : MonoBehaviour
{
static List<string> buildList = new List<string>();
static Dictionary<string, string> DicDependencies = new Dictionary<string, string>();
static Dictionary<string, List<string>> mapDep = new Dictionary<string, List<string>>();
static List<string> m_listNeedCompress = new List<string>();
static Dictionary<string, string> mapMd5Files = new Dictionary<string, string>();
#if UNITY_ANDROID
static string SavePath = Application.streamingAssetsPath + "/Android/BundleRes400/data";
#endif
#if UNITY_IPHONE
static string SavePath = Application.streamingAssetsPath + "/IOS/BundleRes400/data";
#endif
#if UNITY_STANDALONE_WIN
static string SavePath = Application.streamingAssetsPath + "/PC/BundleRes400/data";
#endif
static string SourcePath = "Assets";
static string strColletion = "col_";
static string outFormat = ".assetBundle";
static int nDependenciesCount = 0;
static BuildAssetBundleOptions buildOp = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.UncompressedAssetBundle | BuildAssetBundleOptions.CompleteAssets
| BuildAssetBundleOptions.DeterministicAssetBundle;
static BuildOptions opScene = BuildOptions.BuildAdditionalStreamedScenes | BuildOptions.UncompressedAssetBundle;
static BuildAssetBundleOptions buildOpNoComplete = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.CompleteAssets;
// BuildAssetBundleOptions buildOp = /*(BMDataAccessor.BMConfiger.compress ? 0 : BuildAssetBundleOptions.UncompressedAssetBundle) |*/
// (BMDataAccessor.BMConfiger.deterministicBundle ? 0 : BuildAssetBundleOptions.DeterministicAssetBundle) |
// BuildAssetBundleOptions.CollectDependencies;
#if UNITY_ANDROID
static BuildTarget target = BuildTarget.Android;
#endif
#if UNITY_IPHONE
#if UNITY_5
static BuildTarget target = BuildTarget.iOS;
#else
static BuildTarget target = BuildTarget.iPhone;
#endif
#endif
#if UNITY_STANDALONE_WIN
static BuildTarget target = BuildTarget.StandaloneWindows;
#endif
[MenuItem("Assets/Build Assert Bundle 4.0.1/Build All")]
static void BuildAll()
{
Clear();
#if TEST
BuildPath(SourcePath, buildOp);
#else
BuildPath(SourcePath + "/Resources", buildOp);
#endif
string[] path = Directory.GetDirectories(SourcePath + "/Games");
foreach (var p in path)
{
#if TEST
BuildPath(p, buildOp);
#else
BuildPath(p + "/Resources", buildOp);
#endif
}
UpdateDependFile(SavePath);
LZMPEditHelper.CompressAllFile_(SavePath);
MD5Tools.GenAllMd5File(SavePath.Replace("/data", "/zip"));
EditorUtility.DisplayDialog("", "Completed", "OK");
AssetDatabase.Refresh();
}
[MenuItem("Assets/Build Assert Bundle 4.0.1/Build Path")]
static void BuildPath()
{
Clear();
GetLocalDependFile(SavePath);
GetLocalMd5File(SavePath);
string filePath = "";
Dictionary<string, Object> map = new Dictionary<string, Object>();
UnityEngine.Object[] arr5 = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets);
if (arr5 == null || arr5.Length <= 0)
return;
foreach (Object obj in arr5)
{
filePath = AssetDatabase.GetAssetPath(obj);
BuildPath(filePath + "/Resources", buildOp);
}
UpdateDependFile(SavePath);
LZMPEditHelper.CompressAllFile_(SavePath);
MD5Tools.GenAllMd5File(SavePath.Replace("/data", "/zip"));
EditorUtility.DisplayDialog("", "Completed", "OK");
AssetDatabase.Refresh();
}
[MenuItem("Assets/Build Assert Bundle 4.0.1/Build Sel File")]
static void BuildSelFile()
{
Clear();
GetLocalDependFile(SavePath);
GetLocalMd5File(SavePath);
string filePath = "";
Dictionary<string, Object> map = new Dictionary<string, Object>();
foreach (Object obj in Selection.objects)
{
filePath = AssetDatabase.GetAssetPath(obj);
if (filePath.EndsWith(".meta"))
continue;
Object p2Asset = AssetDatabase.LoadMainAssetAtPath(filePath);
//Object[] all = AssetDatabase.LoadAllAssetsAtPath(filePath);
map[filePath] = p2Asset;
}
foreach (var item in map)
{
BuildBundle(item.Key, item.Value, buildOp);
}
UpdateDependFile(SavePath);
//LZMPEditHelper.CompressAllFile_(SavePath);
CompressFiles();
//MD5Tools.GenAllMd5File(SavePath.Replace("/data", "/zip"));
EditorUtility.DisplayDialog("", "Completed", "OK");
AssetDatabase.Refresh();
}
[MenuItem("Assets/Build Assert Bundle 4.0.1/Build Scenes")]
static void BuildScenes()
{
Clear();
GetLocalDependFile(SavePath);
GetLocalMd5File(SavePath);
foreach (var item in EditorBuildSettings.scenes)
{
if (item.enabled == false)
continue;
if (!item.path.Contains("main"))
BuildBundle(item.path, null, buildOp);
}
UpdateDependFile(SavePath);
CompressFiles();
//LZMPEditHelper.CompressAllFile_(SavePath);
MD5Tools.GenAllMd5File(SavePath.Replace("/data", "/zip"));
EditorUtility.DisplayDialog("", "Completed", "OK");
AssetDatabase.Refresh();
}
static void BuildSceneBundle(string path, Object[] obj, BuildAssetBundleOptions options)
{
string[] DependArr = AssetDatabase.GetDependencies(new string[] { path });
Debug.Log(DependArr[0]);
}
static void BuildBundle(string[] path, Object[] obj, BuildAssetBundleOptions options)
{
for (int i = 0; i < path.Length; i++)
{
getMapDep(path[i]);
}
refreshMap();
BuildPipeline.PushAssetDependencies();
List<string> listTmp = new List<string>();
BuildBundle_1(path, obj, options, ref listTmp);
listTmp.Clear();
BuildPipeline.PopAssetDependencies();
}
static void getMapDep(string path)
{
string[] DependArr = AssetDatabase.GetDependencies(new string[] { path });
foreach (var item in DependArr)
{
string it = item.Replace('\\', '/');
if (it.EndsWith(".cs"))
continue;
if (path.Equals(it))
{
continue;
}
if (!mapDep.ContainsKey(it))
mapDep[it] = new List<string>();
if (!mapDep[it].Contains(path))
mapDep[it].Add(path);
string[] childDependArr = AssetDatabase.GetDependencies(new string[] { it });
if (childDependArr != null && childDependArr.Length > 1 && null == System.Array.Find(new string[] { path }, p => { return p == it; }))
{
getMapDep(it);
}
}
}
static void refreshMap()
{
List<string> list_key = new List<string>();
foreach (var item in mapDep)
{
list_key.Add(item.Key);
}
foreach (var item in list_key)
mapDep[item] = refreshList(mapDep[item]);
}
static List<string> refreshList(List<string> list)
{
bool isNeedRefresh = false;
foreach (var item in list)
{
for (int i = list.Count - 1; i >= 0; i--)
{
if (mapDep.ContainsKey(item) && mapDep[item].Contains(list[i]))
{
list.Remove(list[i]);
isNeedRefresh = true;
}
}
if (isNeedRefresh)
break;
}
if (isNeedRefresh)
refreshList(list);
return list;
}
static void BuildBundleMapDep(string[] path, Object[] obj, BuildAssetBundleOptions options, ref List<string> listTmp)
{
foreach (var item in mapDep)
{
BuildPipeline.PushAssetDependencies();
// bulid key
foreach (var list in item.Value)
{
BuildPipeline.PushAssetDependencies();
//BuildBundleMapDep();
BuildPipeline.PopAssetDependencies();
}
BuildPipeline.PopAssetDependencies();
}
}
static void BuildBundle_1(string[] path, Object[] obj, BuildAssetBundleOptions options, ref List<string> listTmp)
{
string[] DependArr = AssetDatabase.GetDependencies(path);
string dependinfo = "";
for (int i = 0; i < DependArr.Length; i++)
{
if (!DependArr[i].EndsWith(".cs") && null == System.Array.Find(path, p => { return p == DependArr[i]; }))
{
dependinfo += rePath(DependArr[i]) + ";";
}
}
if (!string.IsNullOrEmpty(dependinfo))
{
DicDependencies[rePath(path[0])] = dependinfo;
}
foreach (var item in DependArr)
{
string it = item.Replace('\\', '/');
if (it.EndsWith(".cs"))
continue;
if (null != System.Array.Find(path, p => { return p == it; }))
{
continue;
}
//if (buildList.Contains(getFileName(it)))
// continue;
if (listTmp.Contains(getFileName(it)))
continue;
string[] childDependArr = AssetDatabase.GetDependencies(new string[] { it });
if (childDependArr != null && childDependArr.Length > 1 && null == System.Array.Find(path, p => { return p == it; }))
{
BuildBundle(new string[] { it }, new Object[] { AssetDatabase.LoadMainAssetAtPath(it) }, options);
//BuildPipeline.PushAssetDependencies();
continue;
}
uint crc = 0;
bool ret = false;
if (it.EndsWith(".unity"))
{
string re = BuildPipeline.BuildStreamedSceneAssetBundle(new string[] { it }, reNameFile(it), target, out crc, opScene);
Debug.LogError("BuildSceneAssetBundle error : " + re);
ret = true;
}
else
{
Object[] objs = AssetDatabase.LoadAllAssetsAtPath(it);
ret = BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(it), objs, reNameFile(it), out crc, options, target);
}
if (ret == false)
{
Debug.LogError("BuildAssetBundle error : " + crc.ToString());
return;
}
if (!m_listNeedCompress.Contains(reNameFile(it)))
m_listNeedCompress.Add(reNameFile(it));
Debug.Log("BuildAssetBundleEx success: outpath=" + reNameFile(it));
if (!buildList.Contains(getFileName(it)))
buildList.Add(getFileName(it));
listTmp.Add(getFileName(it));
}
foreach (var item in path)
{
string it = item.Replace('\\', '/');
//if (buildList.Contains(getFileName(it)))
// continue;
if (listTmp.Contains(getFileName(it)))
continue;
uint crc = 0;
bool ret = false;
if (it.EndsWith(".unity")) // 场景
{
BuildPipeline.BuildStreamedSceneAssetBundle(new string[] { it }, reNameFile(it), target, out crc, opScene);
ret = true;
}
else
ret = BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(it), null, reNameFile(it), out crc, options, target);
if (ret == false)
{
Debug.LogError("BuildAssetBundle error : " + crc.ToString());
return;
}
if (!m_listNeedCompress.Contains(reNameFile(it)))
m_listNeedCompress.Add(reNameFile(it));
Debug.Log("BuildAssetBundleEx success: outpath=" + reNameFile(it));
if (!buildList.Contains(getFileName(it)))
buildList.Add(getFileName(it));
listTmp.Add(getFileName(it));
}
}
static void BuildBundle(string path, Object obj, BuildAssetBundleOptions options)
{
BuildBundle(new string[] { path }, new Object[] { obj }, options);
}
static void BulidBundle(Dictionary<string, Dictionary<string, Object>> mapRootBundle_, BuildAssetBundleOptions options)
{
foreach (var map in mapRootBundle_.Values)
{
foreach (var item in map)
{
BuildBundle(item.Key, item.Value, options);
}
}
}
static void BuildPath(string path, BuildAssetBundleOptions options)
{
string rootPath = path;
Dictionary<string, Dictionary<string, Object>> mapRootBundle = new Dictionary<string, Dictionary<string, Object>>();
getPathObj(rootPath, ref mapRootBundle);
BulidBundle(mapRootBundle, options);
// 打包Base
// if (path != SourcePath)
// {
// string rootPath_ = SourcePath + "/Resources/Base";
// Dictionary<string, Dictionary<string, Object>> mapRootBundle_ = new Dictionary<string, Dictionary<string, Object>>();
// getPathObj(rootPath_, ref mapRootBundle_);
// BulidBundle(mapRootBundle_, options);
// }
// string rootPath = path + "/Base";
// Dictionary<string, Dictionary<string, Object>> mapRootBundle = new Dictionary<string, Dictionary<string, Object>>();
// getPathObj(rootPath, ref mapRootBundle);
//
// BulidBundle(mapRootBundle, options);
// // 打包根目录下资源 // 默认依赖Base
// rootPath = path + "/StaticPic"; // 打包Texture
// mapRootBundle = new Dictionary<string, Dictionary<string, Object>>();
// getPathObj(rootPath, ref mapRootBundle);
// BulidBundle(mapRootBundle, options);
//
// // 打包动态资源(没有依赖)
// rootPath = path + "/DynamicRes";
// mapRootBundle = new Dictionary<string, Dictionary<string, Object>>();
// getPathObj(rootPath, ref mapRootBundle);
// BulidBundle(mapRootBundle, options);
//
// // 打包prefabs (依赖于base)
// rootPath = path + "/Prefabs";
// mapRootBundle = new Dictionary<string, Dictionary<string, Object>>();
// getPathObj(rootPath, ref mapRootBundle);
// BulidBundle(mapRootBundle, options);
}
static void getPathObj(string path, ref Dictionary<string, Dictionary<string, Object>> mapBundle)
{
if (path != null)
{
string[] f1 = null;
string[] d1 = null;
try
{
f1 = Directory.GetFiles(path, "*");
}
catch (System.Exception e)
{
Debuger.LogError("GetFiles error : " + e.ToString());
return;
}
Dictionary<string, Object> mapTmp = new Dictionary<string, Object>();
foreach (string f11 in f1)
{
// GUILayout.TextField(f11);
if (f11.EndsWith(".meta"))
continue;
if (target != BuildTarget.StandaloneWindows && f11.Contains("PC"))
continue;
else if (target != BuildTarget.Android && f11.Contains("Android"))
continue;
else if (target != BuildTarget.iPhone && f11.Contains("IOS"))
continue;
Object p2Asset = AssetDatabase.LoadMainAssetAtPath(f11);
mapTmp[f11.Replace('\\', '/')] = p2Asset;
Debuger.Log(f11);
}
try
{
d1 = Directory.GetDirectories(path);
foreach (string d11 in d1)
{
try
{
getPathObj(d11, ref mapBundle);
}
catch (System.Exception e)
{
Debuger.LogError("getPathObj error : " + e.ToString());
return;
}
}
}
catch (System.Exception e)
{
Debuger.LogError("GetDirectories error : " + e.ToString());
return;
}
if (mapTmp.Count > 0)
{
mapBundle[path.Replace('\\', '/')] = mapTmp;
}
}
}
static string reNameFile(string outpath)
{
string ret = outpath;
ret = ret.Replace('\\', '/');
if (string.IsNullOrEmpty(ret))
{
ret = SavePath;
}
else
ret = ret.Replace(SourcePath, SavePath);
ret = rePath(ret);
string path = ret.Substring(0, ret.LastIndexOf('/'));
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
if (!ret.EndsWith(outFormat))
{
//ret = ret.Substring(0, ret.LastIndexOf('.'));
ret += outFormat;
}
//Debug.Log("reNameFile:" + ret);
// 检查同名资源
while (true)
{
bool isHaveSame = false;
foreach (var item in mapMd5Files)
{
string filename0 = getFileName(item.Key);
string filename1 = getFileName(ret);
if (filename0 == filename1 && !item.Key.Contains(ret))
{
ret = ret.Replace(outFormat, "");
ret += "_";
ret += outFormat;
isHaveSame = true;
}
}
if (!isHaveSame)
break;
}
mapMd5Files[ret] = ""; // 值不做实际操作
return ret;
}
static void PopAssetDependencies()
{
for (int i = 0; i < nDependenciesCount; i++)
{
BuildPipeline.PopAssetDependencies();
}
}
static void CompressFiles()
{
for (int i = 0; i < m_listNeedCompress.Count; i++)
{
string zipPaht = SavePath.Replace("/data", "/zip");
string tmpPath = (m_listNeedCompress[i] + ".zip").Replace(SavePath, zipPaht);
LZMPEditHelper.CompressFileLZMAThread(m_listNeedCompress[i], tmpPath);
}
LZMPEditHelper.CompressFileLZMAThread(SavePath + "/Files.assetBundle", (SavePath + "/Files.assetBundle.zip").Replace("/data/", "/zip/"));
}
static void Clear()
{
buildList.Clear();
nDependenciesCount = 0;
DicDependencies.Clear();
m_listNeedCompress.Clear();
}
static void GetLocalDependFile(string path)
{
try
{
string zipPath = "";
int nIndex = path.LastIndexOf("/data");
if (nIndex != -1)
zipPath = path.Substring(0, nIndex) + "/zip";
LZMPEditHelper.DecompressFile(zipPath + "/Files" + outFormat + ".zip", path + "/Files" + outFormat);
byte[] verTmp = File.ReadAllBytes(path + "/Files" + outFormat);
DicDependencies = JsonMapper.ToObject<Dictionary<string, string>>(Encoding.UTF8.GetString(verTmp));
}
catch (System.Exception e)
{
}
}
static void GetLocalMd5File(string path)
{
try
{
byte[] verTmp = File.ReadAllBytes(path + "/../md5_BundleRes.txt");
mapMd5Files = JsonMapper.ToObject<Dictionary<string, string>>(Encoding.UTF8.GetString(verTmp));
}
catch (System.Exception e)
{
}
}
static void UpdateDependFile(string path)
{
string dataJson = JsonMapper.ToJson(DicDependencies);
File.WriteAllBytes(path + "/Files" + outFormat, new UTF8Encoding().GetBytes(dataJson));
}
static string getFileName(string path)
{
path = path.Replace('\\', '/');
if (path.Contains("/"))
path = path.Substring(path.LastIndexOf('/'));
return path;
}
static string rePath(string path)
{
string ret = path.Replace('\\', '/');
ret = ret.Replace(' ', '_');
return ret;
}
}