一、Unity AssetBundle分包流程图
二、源码
AssetBundleGroupGetter对象,负责分组管理
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class AssetBundleGroupGetter
{
/// <summary>
/// 所有资源的AssetInfo
/// </summary>
private static Dictionary<string, AssetInfo> assetInfoDict = new Dictionary<string, AssetInfo>();
/// <summary>
/// 分过组的AssetInfo key=>头部文件 value=>所有仅被头部文件引用的子依赖列表
/// </summary>
private static Dictionary<AssetInfo, List<AssetInfo>> assetGroupInfoDict =
new Dictionary<AssetInfo, List<AssetInfo>>();
private static string progressTitle;
private static float progress;
public static List<List<string>> GetAllUncompressedGroup()
{
List<List<string>> list = new List<List<string>>();
int index = 0;
foreach(var ais in assetGroupInfoDict)
{
if(ais.Key.assetType != AssetType.Video) continue;
index = list.Count;
list.Add(new List<string>());
list[index].Add(ais.Key.path);
foreach(var ai in ais.Value)
{
list[index].Add(ai.path);
}
}
return list;
}
public static List<List<string>> GetAllGroup()
{
List<List<string>> list = new List<List<string>>();
int index = 0;
foreach(var ais in assetGroupInfoDict)
{
if(ais.Key.assetType == AssetType.Video) continue;
index = list.Count;
list.Add(new List<string>());
if(ais.Key.assetType != AssetType.SpriteAtlas)
list[index].Add(ais.Key.path);
foreach(var ai in ais.Value)
{
list[index].Add(ai.path);
}
}
return list;
}
/// <summary>
/// 打Bundle初始化
/// </summary>
public static void InitBuild(string assetPath)
{
assetInfoDict.Clear();
assetGroupInfoDict.Clear();
progressTitle = "分析依赖";
InitAllAssetInfo(assetPath);
progressTitle = "资源分组";
AllAssetInfoToGroup();
EditorUtility.ClearProgressBar();
}
private static void ShowProgress(string title, string info, float progress)
{
EditorUtility.DisplayProgressBar(title, info, progress);
}
/// <summary>
/// 实例化所有的AssetInfo
/// </summary>
private static void InitAllAssetInfo(string assetPath)
{
string[] allFileArr = Directory.GetFiles(assetPath, "*", SearchOption.AllDirectories);
int allFileCount = allFileArr == null ? 0 : allFileArr.Length;
for (int i = 0; i < allFileCount; i++)
{
string filePath = allFileArr[i];
progress = (float) i / allFileCount;
ShowProgress(progressTitle, filePath, progress);
if (filePath.EndsWith(".meta"))
{
continue;
}
filePath = filePath.Replace("\\", "/");
CreateAssetInfo(filePath, null);
}
}
/// <summary>
/// 生成AssetInfo
/// </summary>
/// <param name="path">AssetInfo的路径</param>
/// <param name="parent">父依赖</param>
private static void CreateAssetInfo(string path, AssetInfo parent = null)
{
AssetInfo info = null;
if (assetInfoDict.ContainsKey(path))
{
info = assetInfoDict[path];
}
else
{
info = new AssetInfo(path);
assetInfoDict.Add(path, info);
}
if (parent != null)
{
parent.AddChild(info);
//文件的父级Scene文件不记录依赖关系
if (parent.assetType != AssetType.Scene)
{
info.AddParent(parent);
}
}
List<string> depends = info.GetDepends();
if (depends != null && depends.Count > 0)
{
foreach (var item in depends)
{
CreateAssetInfo(item, info);
}
}
}
/// <summary>
/// 对所有AssetInfo进行分组
/// </summary>
private static void AllAssetInfoToGroup()
{
int assetCount = assetInfoDict == null ? 0 : assetInfoDict.Count;
int id = 0;
if (assetCount > 0)
{
foreach (var item in assetInfoDict)
{
AssetInfo info = item.Value;
AssetInfoToGroup(info);
progress = (float) id / assetCount;
ShowProgress(progressTitle, info.path, progress);
id++;
}
}
}
/// <summary>
/// AssetInfo进行分组
/// </summary>
/// <param name="info"></param>
private static void AssetInfoToGroup(AssetInfo info)
{
if (info.isGrouped)
{
return;
}
bool isTop = info.IsTop();
if (isTop) //是顶级文件
{
AddAssetGroupInfoDict(info, null);
return;
}
AssetInfo atlasInfo = info.GetBelongSpriteAtlas();
if (atlasInfo != null) //此文件已经被打进了图集
{
AddAssetGroupInfoDict(atlasInfo, info);
return;
}
List<AssetInfo> topParentList = info.FindTopParent();
if (topParentList.Count == 1) //只有一个顶级父文件,那么这个文件和顶级父文件分成一个组
{
AddAssetGroupInfoDict(topParentList[0], info);
}
else
{
//所有当前文件的顶级父物体共同拥有的子依赖
List<AssetInfo> crossChildInfo = FindMultipleParentCrossChilds(topParentList);
for (int i = crossChildInfo.Count - 1; i >= 0; i--)
{
AssetInfo crossInfo = crossChildInfo[i];
if (crossInfo.isGrouped)
{
crossChildInfo.Remove(crossInfo);
continue;
}
List<AssetInfo> crossChildInfoTopParentList = crossInfo.FindTopParent();
int count = crossChildInfoTopParentList == null ? 0 : crossChildInfoTopParentList.Count;
if (count != topParentList.Count) //这个共同子文件中间也被其他顶级文件引用
{
crossChildInfo.Remove(crossInfo);
}
}
//把这些被多个顶级父文件共同依赖的文件分成一组
if (crossChildInfo.Count > 0)
{
//随表找一个文件来占分组字典的坑位
AssetInfo headInfo = crossChildInfo[0];
crossChildInfo.RemoveAt(0);
AddAssetListGroupInfoDict(headInfo, crossChildInfo);
}
}
}
/// <summary>
/// 将文件加入分组字典
/// </summary>
/// <param name="headInfo">头部文件,即为Key</param>
/// <param name="childInfo">依赖文件</param>
private static void AddAssetGroupInfoDict(AssetInfo headInfo, AssetInfo childInfo)
{
if (headInfo == null)
{
return;
}
if (!assetGroupInfoDict.ContainsKey(headInfo))
{
assetGroupInfoDict.Add(headInfo, new List<AssetInfo>());
headInfo.isGrouped = true;
}
if (childInfo != null && !childInfo.isGrouped)
{
assetGroupInfoDict[headInfo].Add(childInfo);
childInfo.isGrouped = true;
}
}
private static void AddAssetListGroupInfoDict(AssetInfo headInfo, List<AssetInfo> childInfoList)
{
if (childInfoList == null || childInfoList.Count == 0)
{
AddAssetGroupInfoDict(headInfo, null);
return;
}
for (int i = 0; i < childInfoList.Count; i++)
{
AssetInfo childInfo = childInfoList[i];
AddAssetGroupInfoDict(headInfo, childInfo);
}
}
/// <summary>
/// 获取多个父文件共同依赖的子文件
/// </summary>
/// <param name="parentList"></param>
/// <returns></returns>
private static List<AssetInfo> FindMultipleParentCrossChilds(List<AssetInfo> parents)
{
if (parents == null || parents.Count < 2)
{
return null;
}
List<AssetInfo> list = parents[0].childList;
if (list == null || list.Count == 0)
{
return null;
}
for (int i = 1; i < parents.Count; i++)
{
List<AssetInfo> childs = parents[i].childList;
if (childs == null || childs.Count == 0)
{
return null;
}
list = list.Intersect(childs).ToList();
if (list == null || list.Count == 0)
{
return null;
}
}
return list;
}
/// <summary>
/// 获得多个子文件共同拥有的父文件
/// </summary>
/// <param name="childs"></param>
/// <returns></returns>
private static List<AssetInfo> FindMultipleChildCrossParents(List<AssetInfo> childs)
{
if (childs == null || childs.Count < 2)
{
return null;
}
List<AssetInfo> list = childs[0].parentList;
if (list == null || list.Count == 0)
{
return null;
}
for (int i = 1; i < childs.Count; i++)
{
List<AssetInfo> parents = childs[i].parentList;
if (parents == null || parents.Count == 0)
{
return null;
}
list = list.Intersect(parents).ToList();
if (list == null || list.Count == 0)
{
return null;
}
}
return list;
}
}
AssetInfo对象 这是一个文件对象,每一个资源文件对应一个AssetInfo
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using Object = UnityEngine.Object;
public enum AssetType{
//图集
SpriteAtlas,
//图片(精灵、贴图)
Image,
//预制体
Prefab,
//音频
Audio,
//视频
Video,
//场景
Scene,
//其他
Other,
}
public class AssetInfo
{
/// <summary>
/// 是否已经分组
/// </summary>
public bool isGrouped = false;
//资源类型
public AssetType assetType;
/// <summary>
/// 文件路径
/// </summary>
public string path;
/// <summary>
/// 所有的父依赖文件
/// </summary>
public List<AssetInfo> parentList;
/// <summary>
/// 所有的子依赖文件
/// </summary>
public List<AssetInfo> childList;
public AssetInfo(string _path)
{
path = _path;
SetAssetType();
}
/// <summary>
/// 设置文件类型
/// </summary>
private void SetAssetType()
{
string suffix = Path.GetExtension(path);
switch (suffix)
{
case ".prefab":
assetType = AssetType.Prefab;
break;
case ".spriteatlas":
assetType = AssetType.SpriteAtlas;
break;
case ".png":
case ".jpg":
case ".psd":
case ".tiff":
case ".tga":
case ".gif":
assetType = AssetType.Image;
break;
case ".mp3":
case ".aif":
case ".wav":
case ".ogg":
assetType = AssetType.Audio;
break;
case ".mp4":
case ".mov":
case ".mpg":
case ".mpeg":
case ".avi":
case ".asf":
assetType = AssetType.Video;
break;
case ".unity":
assetType = AssetType.Scene;
break;
default:
assetType = AssetType.Other;
break;
}
}
/// <summary>
/// 添加父级文件
/// </summary>
/// <param name="info"></param>
public void AddParent(AssetInfo info)
{
if (parentList==null)
{
parentList= new List<AssetInfo>();
}
if (!parentList.Contains(info))
{
parentList.Add(info);
}
}
/// <summary>
/// 添加子级文件
/// </summary>
/// <param name="info"></param>
public void AddChild(AssetInfo info)
{
if (childList==null)
{
childList= new List<AssetInfo>();
}
if (!childList.Contains(info))
{
childList.Add(info);
}
}
/// <summary>
/// 得到最顶层的父级
/// </summary>
/// <returns></returns>
public List<AssetInfo> FindTopParent()
{
List<AssetInfo> list = new List<AssetInfo>();
if (IsTop())
{
return list;
}
for (int i = 0; i < parentList.Count; i++)
{
AssetInfo info = parentList[i];
if (info.parentList==null||info.parentList.Count==0)
{
list.Add(info);
}
}
return list;
}
/// <summary>
/// 是不是顶级文件
/// </summary>
/// <returns></returns>
public bool IsTop()
{
return parentList==null||parentList.Count==0;
}
/// <summary>
/// 得到该文件所属的图集文件
/// </summary>
/// <returns></returns>
public AssetInfo GetBelongSpriteAtlas()
{
AssetInfo atlasInfo = null;
if (assetType!=AssetType.Image||IsTop())
{
return atlasInfo;
}
for (int i = 0; i < parentList.Count; i++)
{
AssetInfo parentInfo = parentList[i];
if (parentInfo.assetType==AssetType.SpriteAtlas)
{
atlasInfo = parentInfo;
break;
}
}
return atlasInfo;
}
/// <summary>
/// 得到最底层的子依赖
/// </summary>
/// <returns></returns>
public List<AssetInfo> FindBottomChild()
{
List<AssetInfo> list = new List<AssetInfo>();
if (childList==null||childList.Count==0)
{
return list;
}
for (int i = 0; i < parentList.Count; i++)
{
AssetInfo info = childList[i];
if (info.childList==null||info.childList.Count==0)
{
list.Add(info);
}
}
return list;
}
/// <summary>
/// 获取目标物体所有的依赖
/// </summary>
/// <param name="objPath">目标物体路径(Assets/xx/xx.abc)</param>
/// <returns></returns>
public List<string> GetDepends()
{
List<string> list = null;
string[] dependArr =AssetDatabase.GetDependencies(path,true);
if (dependArr!=null&&dependArr.Length>0)
{
list=new List<string>();
foreach (var dependPath in dependArr)
{
if (string.IsNullOrEmpty(Path.GetExtension(dependPath)))
continue;
Object obj = AssetDatabase.LoadMainAssetAtPath(dependPath);
if (obj is MonoScript || obj is LightingDataAsset)
continue;
if (dependPath == path)
continue;
if (dependPath.EndsWith(".cs"))
continue;
if (!dependPath.StartsWith("Assets"))
continue;
if (!list.Contains(dependPath))
{
list.Add(dependPath);
}
}
}
return list;
}
}
|