using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace Framework
{
// 创建资源AB配置菜单
[CreateAssetMenu(fileName = "ABConfig", menuName = "CreateABConfig", order = 999)]
public class ABConfig : ScriptableObject
{
/// <summary>
/// 需要打包的文件夹路径,会遍历这个文件夹下的所有文件和文件夹,
/// 该目录下的所有文件名和文件夹名不能重复,必须保证名字的唯一性。
/// 直接定位到父目录,用子文件夹名当ab包名,如果根目录有文件用父目录单独打包。
/// </summary>
[Header("AB包配置根目录(根目录下所有文件夹全部分开打包)")]
public List<string> RootABList = new List<string>();
[Header("真正的AB包打包列表(自动生成)")]
public List<ABData> TrueABList = new List<ABData>();
[System.Serializable]
public class ABData
{
public string path;
public string abName; // ab包名
//public bool must;
}
/// <summary>
/// 设置打包名和路径
/// </summary>
public List<ABData> SetABNameAndPath()
{
//所有AB包的路径 AB包路径,AB包名
var allABPathList = new List<KeyValuePair<string, string>>();
//需要删除Assets目录之前的Length
int needDeleteLength = Application.dataPath.Length - "Assets".Length;
//遍历输入的目录
for (int i = 0; i < RootABList.Count; i++)
{
var rootHasFile = false;
var path = RootABList[i];
var pathDir = new DirectoryInfo(path);
//循环目录下的文件和子目录 但是不包含子目录下的文件和子目录
foreach (var item in pathDir.GetFileSystemInfos("*.*", SearchOption.TopDirectoryOnly))
{
var childDir = item as DirectoryInfo;
//空值就代表它是文件
if (childDir == null)
{
//场景需要单独打包
if (item.Extension == ".unity")
{
allABPathList.Add(new KeyValuePair<string, string>(item.FullName.Substring(needDeleteLength).Replace("\\", "/"), item.Name.Replace(".unity","")));
}
//跳过meta文件
else if (item.Extension != ".meta")
{
//根目录下也有文件 说明当前路径也需要单独打个包,注意 不要包含其它子文件夹下的文件
rootHasFile = true;
}
}
else //文件夹
{
allABPathList.Add(new KeyValuePair<string, string>(item.FullName.Substring(needDeleteLength).Replace("\\", "/"), pathDir.Name + childDir.Name));
}
}
// 只有在根目录下有文件的时候才会打包根目录
if (rootHasFile)
{
allABPathList.Add(new KeyValuePair<string, string>(path, pathDir.Name));
}
}
TrueABList.Clear();
for (int i = 0; i < allABPathList.Count; i++)
{
var kv = allABPathList[i];
var data = new ABData();
data.path = kv.Key;
data.abName = kv.Value.ToLower();
TrueABList.Add(data);
}
UnityEditor.EditorUtility.SetDirty(this);
return TrueABList;
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Framework
{
[CustomEditor(typeof(ABConfig))]
public class ABConfiInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
ABConfig abconfig = target as ABConfig;
if (GUILayout.Button("生成真正的AB包列表"))
{
abconfig.SetABNameAndPath();
}
}
}
}
using LitJson;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Framework
{
/// <summary>
/// AB包生成
/// </summary>
public class BuildAssetBundles : Editor
{
/// <summary>
/// AB包输出路径
/// </summary>
public static string ABOutPath = Application.dataPath.Substring(0,Application.dataPath .Length - "Assets".Length) + "AssetBundle";
/// <summary>
/// 脚本DLL输出路径
/// </summary>
public static string ScriptOutPath = Application.dataPath.Substring(0,Application.dataPath .Length - "Assets".Length) + "HybridCLRData/HotUpdateDlls/" + EditorUserBuildSettings.activeBuildTarget .ToString();
//依赖信息Json路径
private static string JSONINFORMATIONPATH = "Assets/Editor/ABConfig/JsonInformation/AssetInformation.json";
//ABConfig.Asset
private static string ABCONFIGPATH = "Assets/Editor/ABConfig/ABConfig.asset";
private static ABConfig _abConfig => AssetDatabase.LoadAssetAtPath<ABConfig>(ABCONFIGPATH);
[MenuItem("YSFramework/Build AssetBundles")]
public static void BuildAsset()
{
//1.设置AB包路径和AB包名
_abConfig.SetABNameAndPath();
//2.清空文件夹
if (Directory.Exists(ABOutPath))
{
Directory.Delete(ABOutPath, true);
}
Directory.CreateDirectory(ABOutPath);
//3.设置打包名通过路径
SetABNameByPath();
//4.存储依赖关系表(如果用Unity自带的依赖管理可以忽略)
SaveABPackageToJson();
//5.真正打ab包的地方,打包实际上就是一句话
BuildPipeline.BuildAssetBundles(ABOutPath, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);
//6.把不需要的依赖文件删除
DeleteRelyOnFile();
//7.生成MD5校验文件
SaveABMD5ToXML();
//8.清除AB包标签
ClearABName();
//打开文件夹
OpenFileTools.OpenFile(ABOutPath);
//回收资源
System.GC.Collect();
//刷新编辑器
AssetDatabase.Refresh();
}
/// <summary>
/// 设置AB包路径的AB包名
/// </summary>
/// <param name="abConfig"></param>
private static void SetABNameByPath()
{
var list = _abConfig.TrueABList;
foreach (var item in list)
{
AssetImporter.GetAtPath(item.path).assetBundleName = item.abName;
}
}
/// <summary>
/// 存储依赖关系为Json格式
/// </summary>
private static void SaveABPackageToJson()
{
Directory.CreateDirectory("Assets/Editor/ABConfig/JsonInformation");
File.Delete(JSONINFORMATIONPATH);
var list = _abConfig.TrueABList;
var abInfo = new ABInfo();
foreach (var ab in list)
{
//当前ab包依赖的列表
var relyOnList = AssetDatabase.GetAssetBundleDependencies(ab.abName, true).ToList();
abInfo.ABRelyInfoList.Add(new ABInfo.ABRelyInfo()
{
ABName = ab.abName,
//ABPath = ab.path,
ABRelyOnNameList = relyOnList,
});
//当前ab包包括的资源路径
foreach (var item in AssetDatabase.GetAssetPathsFromAssetBundle(ab.abName))
{
abInfo.ABFileDic.Add(item.Substring(item.LastIndexOf("/") + 1), ab.abName);
}
}
//存储完毕后把这个文件也打个包 读取时首先读取这个AB包
using (var assetInformation = File.CreateText(JSONINFORMATIONPATH))
{
assetInformation.Write(JsonMapper.ToJson(abInfo));
}
}
/// <summary>
/// 删除不需要的依赖文件
/// </summary>
private static void DeleteRelyOnFile()
{
var dir = new DirectoryInfo(ABOutPath);
foreach (var item in dir.GetFileSystemInfos("*", SearchOption.AllDirectories))
{
if(item.Extension == ".manifest")
{
File.Delete(item.FullName);
}
}
File.Delete(ABOutPath + "/AssetBundle");
}
/// <summary>
/// 生成AB包的MD5
/// </summary>
private static void SaveABMD5ToXML()
{
var list = new List<ABMd5Info>();
var dir = new DirectoryInfo(ABOutPath);
foreach (var item in dir.GetFileSystemInfos("*", SearchOption.AllDirectories))
{
var path = ABOutPath + "/" + item.Name;
list.Add(new ABMd5Info()
{
ABName = item.Name,
ABSize = File.ReadAllBytes(path).Length,
ABMd5 = Md5Util.GetMd5ByPath(path),
});
}
Debug.Log(ScriptOutPath);
//添加脚本热更的信息
var dllName = "Assembly-CSharp.dll";
var dllPath = ScriptOutPath + "/" + dllName;
list.Add(new ABMd5Info()
{
ABName = dllName,
ABSize = File.ReadAllBytes(dllPath).Length,
ABMd5 = Md5Util.GetMd5ByPath(dllPath),
});
File.Move(dllPath, ABOutPath + "/" + dllName);
//输出资源索引信息
using (var fileUpdateInfo = File.CreateText(ABOutPath + "/" + "fileUpdateInfo.json"))
{
var json = JsonMapper.ToJson(list);
Debug.Log(json);
fileUpdateInfo.Write(json);
}
}
/// <summary>
/// 清除AB包标签
/// </summary>
private static void ClearABName()
{
foreach (var item in _abConfig.TrueABList)
{
AssetImporter.GetAtPath(item.path).assetBundleName = null;
}
AssetDatabase.RemoveUnusedAssetBundleNames();
foreach (var item in AssetDatabase.GetAllAssetBundleNames())
{
Debug.LogError("AB 标签存在未移除情况:" + item);
}
}
}
}