using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
public class BundleEditor
{
/// <summary>
/// AssetBundleXml文件配置
/// </summary>
private static string AssetbundleConfigPath= Application.dataPath + "/ABData";
/// <summary>
/// 打包文件生成目录
/// </summary>
private static string AssetBundlePath = Application.streamingAssetsPath + "/AssetBundles";
/// <summary>
/// AB包Assets配置文件目录
/// </summary>
private static string ABCONFIGPATH = "Assets/GameAssets/ABConfig.asset";
/// <summary>
/// 所有文件夹ab包dic key是ab包名 value是路径
/// </summary>
private static Dictionary<string, string> m_AllFileDir = new Dictionary<string, string>();
/// <summary>
/// 存储文件夹AB包资源路径 用来判断AB包资源重复文件 进行过滤
/// </summary>
private static List<string> m_AllFileAB = new List<string>();
/// <summary>
/// 单个prefab的独立依赖项 key值就是prefab 名字
/// </summary>
private static Dictionary<string, List<string>> m_PrefabAllDependDir = new Dictionary<string, List<string>>();
/// <summary>
/// 储存所有有效路径
/// </summary>
private static List<string> m_ValueableFilePaths = new List<string>();
[MenuItem("Tool/BulidAB")]
public static void BuildAB()
{
m_ValueableFilePaths.Clear();
m_PrefabAllDependDir.Clear();
m_AllFileAB.Clear();
m_AllFileDir.Clear();
ABConfig abconfig = AssetDatabase.LoadAssetAtPath<ABConfig>(ABCONFIGPATH);
//文件夹打包
foreach (ABConfig.FileDirABName filedir in abconfig.m_AllFileDirAB)
{
if (m_AllFileDir.ContainsKey(filedir.ABName))
{
Debug.LogError("ab 包名字配置重复");
}
else
{
m_AllFileDir.Add(filedir.ABName, filedir.Path);
m_AllFileAB.Add(filedir.Path);
m_ValueableFilePaths.Add(filedir.Path);
}
}
//所有 prefab 资源路径guid
string[] allstr = AssetDatabase.FindAssets("t:Prefab", abconfig.m_AllPrefabPath.ToArray());
for (int i = 0; i < allstr.Length; i++)
{
//guid 转路径
string path = AssetDatabase.GUIDToAssetPath(allstr[i]);
EditorUtility.DisplayProgressBar("查找prefab ", path, i * 1.0f / allstr.Length);
m_ValueableFilePaths.Add(path);
//找到prefab所有依赖项
if (!IsContainAllFileAB(path))
{
GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(path);
//获取所有依赖项 会包含脚本
string[] alldepend = AssetDatabase.GetDependencies(path);
List<string> alldependPathList = new List<string>();
for (int j = 0; j < alldepend.Length; j++)
{
//判断依赖项是否在文件夹ab包资源站中且不是脚本
if (!IsContainAllFileAB(alldepend[j]) && !alldepend[j].EndsWith(".cs"))
{
//添加到过滤列表
m_AllFileAB.Add(alldepend[j]);
//加入当前预制体的依赖队列
alldependPathList.Add(alldepend[j]);
}
}
if (m_PrefabAllDependDir.ContainsKey(obj.name))
Debug.LogError("存在相同名字的 prefab " + obj.name);
else
{
m_PrefabAllDependDir.Add(obj.name, alldependPathList);
}
}
}
//设置文件夹ab包名
foreach (var name in m_AllFileDir.Keys)
{
SetABName(name, m_AllFileDir[name]);
}
//设置prefab ab 包名
foreach (var name in m_PrefabAllDependDir.Keys)
{
SetABName(name, m_PrefabAllDependDir[name]);
}
//构建AssetBundle
BuildAssetBundle();
//获取所有的AB包名
string[] oldABNames = AssetDatabase.GetAllAssetBundleNames();
//清除之前的AB包名 因为会在meta文件中生成对应的依赖关系
for (int i = 0; i < oldABNames.Length; i++)
{
AssetDatabase.RemoveAssetBundleName(oldABNames[i], true);
EditorUtility.DisplayProgressBar("清除AB包名:", oldABNames[i], i * 1.0f / oldABNames.Length);
}
AssetDatabase.Refresh();
EditorUtility.ClearProgressBar();
}
/// <summary>
/// 构建assetbundle文件
/// </summary>
static void BuildAssetBundle()
{
string[] allBundleNames = AssetDatabase.GetAllAssetBundleNames();
//key为全路径 value 为包名 用来生成XML配置
Dictionary<string, string> respathDic = new Dictionary<string, string>();
for (int i = 0; i < allBundleNames.Length; i++)
{
string[] allBundlePath = AssetDatabase.GetAssetPathsFromAssetBundle(allBundleNames[i]);
for (int j = 0; j < allBundlePath.Length; j++)
{
if (allBundlePath[j].EndsWith(".cs") )
continue;
Debug.Log("此AB包:" + allBundleNames[i] + " 下面包含的资源路径:" + allBundlePath[j]);
if (ValidPath(allBundlePath[j]))
respathDic.Add(allBundlePath[j], allBundleNames[i]);
}
}
// 删除没有用的AB包 为了减少打包时间 有时候不是所有资源都要重新打包 要重新打包的资源手动删除或者修改配置文件
DeleteUnUseAsseBundle();
//生成 AB包配置表
WriteABConfig(respathDic);
if (Directory.Exists(Application.streamingAssetsPath))
Directory.CreateDirectory(Application.streamingAssetsPath);
if (!Directory.Exists(AssetBundlePath))
Directory.CreateDirectory(AssetBundlePath);
BuildPipeline.BuildAssetBundles(AssetBundlePath, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);
}
/// <summary>
/// 将配置信息写入AB配置文件
/// </summary>
static void WriteABConfig(Dictionary<string,string> respathDic)
{
AssetBundleConfig assetBundleConfig = new AssetBundleConfig();
assetBundleConfig.ABList = new List<ABBase>();
foreach (string path in respathDic.Keys)
{
ABBase aBBase = new ABBase();
aBBase.Path = path;
aBBase.Crc = Crc32.GetCrc32(path);
aBBase.ABName = respathDic[path];
aBBase.AssetName = path.Remove(0, path.LastIndexOf("/") + 1);
aBBase.ABDependences = new List<string>();
//获取所有依赖项
string[] resdependence = AssetDatabase.GetDependencies(path);
for (int i = 0; i < resdependence.Length; i++)
{
string tempPath = resdependence[i];
//排除自己和 cs脚本
if (tempPath == path || path.EndsWith(".cs")) continue;
string abName = "";
//依赖项处理
if (respathDic.TryGetValue(tempPath, out abName))
{
//如果依赖包已经存在
if (abName == respathDic[path]) continue;
if (!aBBase.ABDependences.Contains(abName))
{
aBBase.ABDependences.Add(abName);
}
}
}
assetBundleConfig.ABList.Add(aBBase);
}
if (!Directory.Exists(AssetbundleConfigPath))
Directory.CreateDirectory(AssetbundleConfigPath);
//写入xml
string xmlpath = AssetbundleConfigPath + "/AssetbundleConfig.xml";
if (File.Exists(xmlpath))
File.Delete(xmlpath);
FileStream fs = new FileStream(xmlpath, FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite);
StreamWriter sw = new StreamWriter(fs,System.Text.Encoding.UTF8);
XmlSerializer xs = new XmlSerializer(assetBundleConfig.GetType());
xs.Serialize(sw,assetBundleConfig);
sw.Close();
fs.Close();
//写入二进制
//优化掉path 路径 path 是用来观察的
foreach (ABBase abbase in assetBundleConfig.ABList)
abbase.Path = "";
string bytepath = AssetbundleConfigPath + "/AssetBundleConfig.bytes";
FileStream fbs = new FileStream(bytepath,FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite);
BinaryFormatter bfm = new BinaryFormatter();
bfm.Serialize(fbs,assetBundleConfig);
fbs.Close();
}
/// <summary>
/// 删除没有用的AB包 为了减少打包时间 有时候不是所有资源都要重新打包 要重新打包的资源手动删除或者修改配置文件
/// </summary>
static void DeleteUnUseAsseBundle()
{
string[] allBundlesName = AssetDatabase.GetAllAssetBundleNames();
DirectoryInfo directoryInfo = new DirectoryInfo(AssetBundlePath);
//获取bunlde目录下的所有文件
FileInfo[] files = directoryInfo.GetFiles("*",SearchOption.AllDirectories);
for (int i = 0; i < files.Length; i++)
{
if (ContainABName(files[i].Name, allBundlesName) || files[i].Name.EndsWith(".meta") || files[i].Name.EndsWith(".manifest") || files[i].Name.EndsWith(".bytes"))
continue;
else
{
Debug.Log("删除无效或过时资源: " + files[i].Name);
if (File.Exists(files[i].FullName))
File.Delete(files[i].FullName);
}
}
}
/// <summary>
///当前存在的bundle 是否存在配置表中配置的包名
/// </summary>
/// <returns></returns>
static bool ContainABName(string abname,string[] allAbNames)
{
for (int i = 0; i < allAbNames.Length; i++)
{
if (abname == allAbNames[i]) return true;
}
return false;
}
/// <summary>
/// 通过代码设置AB包的名字
/// </summary>
static void SetABName(string abName, string path)
{
AssetImporter assetImporter = AssetImporter.GetAtPath(path);
if (assetImporter == null) Debug.LogError("不存在此路径文件" + path);
else
assetImporter.assetBundleName = abName;
}
/// <summary>
/// 通过代码设置AB包的名字
/// </summary>
static void SetABName(string abName, List<string> pathList)
{
for (int i = 0; i < pathList.Count; i++)
{
SetABName(abName, pathList[i]);
}
}
/// <summary>
/// Ab资源是否已经包含
/// </summary>
/// <returns></returns>
static bool IsContainAllFileAB(string abPath)
{
for (int i = 0; i < m_AllFileAB.Count; i++)
{
//test/ testaa/a.prefab
if (m_AllFileAB[i] == abPath || (abPath.Contains(m_AllFileAB[i])&&(abPath.Replace(m_AllFileAB[i],"")[0]=='/')))
return true;
}
return false;
}
/// <summary>
/// 是否是有效路径
/// </summary>
/// <returns></returns>
static bool ValidPath(string path)
{
for (int i = 0; i < m_ValueableFilePaths.Count; i++)
{
if (path.Contains(m_ValueableFilePaths[i])) return true;
}
return false;
}
}
ABConfig asset
![96d8c9cf0f208a85ba9f8dd30b8197bb.png](https://i-blog.csdnimg.cn/blog_migrate/cee6c44cdccabc3882926ce8c0be3e60.png)
![53ac8e238807186114b0904034794932.png](https://i-blog.csdnimg.cn/blog_migrate/e76f7a5b6c8072a35312759bc3968d64.png)
对应脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName ="ABConfig",menuName ="CreateABConfig",order =0)]
public class ABConfig : ScriptableObject
{
/// <summary>
/// 单个文件所在文件夹路径,会遍历这个文件夹下的所有prefab,所有prefab名字不能重复,必须保证名字的唯一性
/// </summary>
public List<string> m_AllPrefabPath = new List<string>();
/// <summary>
/// 所有文件夹AB包
/// </summary>
public List<FileDirABName> m_AllFileDirAB = new List<FileDirABName>();
[System.Serializable]//序列化结构体 字典不能使用该特性进行序列化
public struct FileDirABName
{
public string ABName;
public string Path;
}
}