1、Windows.manifest assetbundle相互依赖的关系 资源包的依赖关系 这种方式把依赖的图集全部加载出来,具体依赖哪个图加载不出来,
2、如果依赖的图集多,大部分图集都要load出来也释放不掉,不利于内存管理
不知道一个实例依赖哪些字体,依赖哪些sprite
3、UITask 在哪个Assetbundle里,同时要知道依赖于哪些asset 这个asset在不在内存里,如果不在对象池里,看这个资源在哪个bundle里,然后加载asset,最后再加载主资源
4、看的见的实例依赖的是asset,asset依赖的是Assetbundle,为了方便存储依赖关系,建一个资源类(AssetEntity),
依赖关系实体
需要加载依赖的时候,根据资源的完成信息名,找到assetEntity然后加载
5、怎么样拿到依赖关系?
#region OnCreateDependenciesFile 生成依赖关系文件
/// <summary>
/// 生成依赖关系文件
/// </summary>
private void OnCreateDependenciesFile()
{
//第一次循环 把所有的Asset存储到一个列表里
//临时列表
List<AssetEntity> tempLst = new List<AssetEntity>();
//循环设置文件夹包括子文件里边的项
for (int i = 0; i < m_List.Count; i++)
{
AssetBundleEntity entity = m_List[i];//取到一个节点
string[] folderArr = new string[entity.PathList.Count];
for (int j = 0; j < entity.PathList.Count; j++)
{
string path = Application.dataPath + "/" + entity.PathList[j];
//Debug.LogError("path=" + path);
CollectFileInfo(tempLst, path);
}
}
//
int len = tempLst.Count;
//资源列表
List<AssetEntity> assetList = new List<AssetEntity>();
//第二次循环来处理依赖关系
for (int i = 0; i < len; i++)
{
AssetEntity entity = tempLst[i];
AssetEntity newEntity = new AssetEntity();
newEntity.Category = entity.Category;
newEntity.AssetName = entity.AssetFullName.Substring(entity.AssetFullName.LastIndexOf("/") + 1);
newEntity.AssetName = newEntity.AssetName.Substring(0, newEntity.AssetName.LastIndexOf("."));
newEntity.AssetFullName = entity.AssetFullName;
newEntity.AssetBundleName = entity.AssetBundleName;
//加到新列表中
assetList.Add(newEntity);
//场景不需要检查依赖项
if (entity.Category == AssetCategory.Scenes)
{
continue;
}
//实例化资源信息依赖项
newEntity.DependsAssetList = new List<AssetDependsEntity>();
//相当于点击右键,选择依赖项
string[] arr = AssetDatabase.GetDependencies(entity.AssetFullName);
foreach (string str in arr)
{
//如果这个资源在临时列表里,说明在download目录下
if (!str.Equals(newEntity.AssetFullName, StringComparison.CurrentCultureIgnoreCase) && GetIsAsset(tempLst, str))
{
//实例化依赖项,然后赋值,加到依赖项列表里面
AssetDependsEntity assetDepends = new AssetDependsEntity();
assetDepends.Category = GetAssetCategory(str);
assetDepends.AssetFullName = str;
//把依赖资源 加入到依赖资源列表
newEntity.DependsAssetList.Add(assetDepends);
}
}
}
//生成一个Json文件,用来观察
string targetPath = Application.dataPath + "/../AssetBundles/" + dal.GetVersion() + "/" + arrBuildTarget[buildTargetIndex];
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
string strJsonFilePath = targetPath + "/AssetInfo.json"; //版本文件路径
IOUtil.CreateTextFile(strJsonFilePath, LitJson.JsonMapper.ToJson(assetList));
Debug.Log("生成 AssetInfo.json 完毕");
//生成一个MMO_MemoryStream,然后写入文件流
MMO_MemoryStream ms = new MMO_MemoryStream();
//生成二进制文件
len = assetList.Count;
ms.WriteInt(len);
//把生成的列表序列化成一个数据流
for (int i = 0; i < len; i++)
{
AssetEntity entity = assetList[i];
ms.WriteByte((byte)entity.Category);
ms.WriteUTF8String(entity.AssetFullName);
ms.WriteUTF8String(entity.AssetBundleName);
if (entity.DependsAssetList != null)
{
//添加依赖资源
int depLen = entity.DependsAssetList.Count;
ms.WriteInt(depLen);
for (int j = 0; j < depLen; j++)
{
AssetDependsEntity assetDepends = entity.DependsAssetList[j];
ms.WriteByte((byte)assetDepends.Category);
ms.WriteUTF8String(assetDepends.AssetFullName);
}
}
else
{
ms.WriteInt(0);
}
}
//压缩成一个AssetInfo.bytes的文件
string filePath = targetPath + "/AssetInfo.bytes"; //版本文件路径
byte[] buffer = ms.ToArray();
//压缩一下
buffer = ZlibHelper.CompressBytes(buffer);
FileStream fs = new FileStream(filePath, FileMode.Create);
fs.Write(buffer, 0, buffer.Length);
fs.Close();
Debug.Log("生成 AssetInfo.bytes 完毕");
}
资源实体
using System.Collections.Generic;
/// <summary>
/// Asset实体
/// </summary>
public class AssetEntity
{
/// <summary>
/// 资源分类
/// </summary>
public AssetCategory Category;
/// <summary>
/// 资源名称
/// </summary>
public string AssetName;
/// <summary>
/// 资源完整名称(路径)
/// </summary>
public string AssetFullName;
/// <summary>
/// 所属资源包(这个资源在哪一个Assetbundle里)
/// </summary>
public string AssetBundleName;
/// <summary>
/// 依赖资源
/// </summary>
public List<AssetDependsEntity> DependsAssetList;
}
资源分类
/// <summary>
/// Asset分类
/// </summary>
public enum AssetCategory
{
/// <summary>
///
/// </summary>
None = 0,
/// <summary>
/// 声音
/// </summary>
Audio = 1,
/// <summary>
/// 自定义Shader
/// </summary>
CusShaders = 2,
/// <summary>
/// 表格
/// </summary>
DataTable = 3,
/// <summary>
/// 特效资源
/// </summary>
EffectSources = 4,
/// <summary>
/// 角色特效预设
/// </summary>
RoleEffectPrefab = 5,
/// <summary>
/// UI特效预设
/// </summary>
UIEffectPrefab = 6,
/// <summary>
/// 角色预设
/// </summary>
RolePrefab = 7,
/// <summary>
/// 角色资源
/// </summary>
RoleSources = 8,
/// <summary>
/// 场景
/// </summary>
Scenes = 9,
/// <summary>
/// 字体
/// </summary>
UIFont = 10,
/// <summary>
/// UI预设
/// </summary>
UIPrefab = 11,
/// <summary>
/// UI资源
/// </summary>
UIRes = 12,
/// <summary>
/// Lua脚本
/// </summary>
xLuaLogic = 13
}
依赖资源类
/// <summary>
/// Asset依赖项实体
/// </summary>
public class AssetDependsEntity
{
/// <summary>
/// 资源分类
/// </summary>
public AssetCategory Category;
/// <summary>
/// 资源完整名称
/// </summary>
public string AssetFullName;
}
循环的列表来源 AssetBundleConfig.xml
/// <summary>
/// 构造函数
/// </summary>
void OnEnable()
{
string xmlPath = Application.dataPath + @"\YouYouFramework\Editor\AssetBundle\AssetBundleConfig.xml";
dal = new AssetBundleDAL(xmlPath);
m_List = dal.GetList();
m_Dic = new Dictionary<string, bool>();
for (int i = 0; i < m_List.Count; i++)
{
m_Dic[m_List[i].Key] = true;
}
}
循环列表,拿到路径,收集文件信息
如果是场景,直接赋值
如果是文件夹,遍历文件夹下所有的子文件,排除掉.meta,排除掉.idea
#region CollectFileInfo 收集文件信息
/// <summary>
/// 收集文件信息
/// </summary>
/// <param name="tempLst"></param>
/// <param name="folderPath"></param>
private void CollectFileInfo(List<AssetEntity> tempLst, string folderPath)
{
//场景
if (folderPath.IndexOf(".unity") != -1)
{
int index = folderPath.IndexOf("Assets/", StringComparison.CurrentCultureIgnoreCase);
//路径
string newPath = folderPath.Substring(index);
AssetImporter import = AssetImporter.GetAtPath(newPath);
AssetEntity entity = new AssetEntity();
entity.AssetFullName = newPath.Replace("\\", "/");
entity.Category = AssetCategory.Scenes;
entity.AssetBundleName = import.assetBundleName + ".assetbundle";
tempLst.Add(entity);
}
else
{
DirectoryInfo directory = new DirectoryInfo(folderPath);
//拿到文件夹下所有文件
FileInfo[] arrFiles = directory.GetFiles("*", SearchOption.AllDirectories);
for (int i = 0; i < arrFiles.Length; i++)
{
FileInfo file = arrFiles[i];
//排除.meta
if (file.Extension == ".meta")
{
continue;
}
string filePath = file.FullName; //全名 包含路径扩展名
//Debug.LogError("filePath=" + filePath);
int index = filePath.IndexOf("Assets\\", StringComparison.CurrentCultureIgnoreCase);
//路径
string newPath = filePath.Substring(index);
//xlua用的idea,会生成idea文件
if (newPath.IndexOf(".idea") != -1) //过滤掉idea文件
{
continue;
}
AssetEntity entity = new AssetEntity();
entity.AssetFullName = newPath.Replace("\\", "/");
//获取资源分类 ,去掉文件名,只保留路径,
entity.Category = GetAssetCategory(newPath.Replace(file.Name, ""));
//获取资源包名称
entity.AssetBundleName = GetAssetBundleName(newPath);
tempLst.Add(entity);
}
}
}
#endregion
获取资源分类
#region GetAssetCategory 获取资源分类
/// <summary>
/// 获取资源分类,路径中包含的
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private AssetCategory GetAssetCategory(string filePath)
{
AssetCategory category = AssetCategory.None;
if (filePath.IndexOf("Audio") != -1)
{
category = AssetCategory.Audio;
}
else if (filePath.IndexOf("CusShaders") != -1)
{
category = AssetCategory.CusShaders;
}
else if (filePath.IndexOf("DataTable") != -1)
{
category = AssetCategory.DataTable;
}
else if (filePath.IndexOf("EffectSources") != -1)
{
category = AssetCategory.EffectSources;
}
else if (filePath.IndexOf("RoleEffectPrefab") != -1)
{
category = AssetCategory.RoleEffectPrefab;
}
else if (filePath.IndexOf("UIEffectPrefab") != -1)
{
category = AssetCategory.UIEffectPrefab;
}
else if (filePath.IndexOf("RolePrefab") != -1)
{
category = AssetCategory.RolePrefab;
}
else if (filePath.IndexOf("RoleSources") != -1)
{
category = AssetCategory.RoleSources;
}
else if (filePath.IndexOf("Scenes") != -1)
{
category = AssetCategory.Scenes;
}
else if (filePath.IndexOf("UIFont") != -1)
{
category = AssetCategory.UIFont;
}
else if (filePath.IndexOf("UIPrefab") != -1)
{
category = AssetCategory.UIPrefab;
}
else if (filePath.IndexOf("UIRes") != -1)
{
category = AssetCategory.UIRes;
}
else if (filePath.IndexOf("xLuaLogic") != -1)
{
category = AssetCategory.xLuaLogic;
}
return category;
}
#endregion
获取资源包名称
#region GetAssetBundleName 获取资源包的短路径
/// <summary>
/// 获取资源包的短路径
/// </summary>
/// <param name="newPath"></param>
/// <returns></returns>
private string GetAssetBundleName(string newPath)
{
//Debug.Log(newPath);
if (newPath.LastIndexOf("\\") == -1) return null;
AssetImporter import = AssetImporter.GetAtPath(newPath);
if (import != null)
{
//直到AssetbundleName不是空的时候
if (!string.IsNullOrEmpty(import.assetBundleName))
{
//Debug.Log(import.assetBundleName);
return import.assetBundleName + ".assetbundle";
}
else
{
//递归寻找上一级目录
string path = newPath.Substring(0, newPath.Replace("\\", "/").LastIndexOf("/"));
return GetAssetBundleName(path);
}
}
return null;
}
#endregion
加载一个预设,先看这个预设在不在池里,在直接取,
如果不在,检查主资源,assetbundle在不在,如果不在,去磁盘load bundle,然后加载主资源,加载主资源之前,还需要检测依赖项,循环依赖项,看依赖项有没全部加载出来,没加载出来,加载依赖项,看依赖项在哪个bundle中,看bundle在不在,如果不在把bundle加载出来,同一个asset,其余UI引用该资源,计数+1,大于1在内存中不会释放,UI销毁的时候对应的计数-1,隔段时间AssetPool检测一次,哪些资源引用计数为0可以释放,从内存中移除掉。