1.依赖打包,先获取依赖关系
先获取所有依赖关系。并且存储在XML文件中,因为我们的项目只需要打包Prefab,所以只需要一维的依赖就够用了
xml version="1.0" encoding="UTF-8"?>
<root>
<resource AssetBundleName="cc5f40250fc1dad43a33c9b9b31001dd" AssetName="yujing_yuan_30" UnityType="UnityEngine.GameObject">
<child AssetBundleName="38d758a33f8f4a147be81f1f80e647b4" AssetName="glow_00143" UnityType="UnityEngine.Material" />
<child AssetBundleName="ad78e334b10df0a4ea65f9ac040ee59a" AssetName="glow_00143" UnityType="UnityEngine.Texture2D" />
<child AssetBundleName="90642d347cb46c942a94cf1bbeba3aba" AssetName="glow_00175" UnityType="UnityEngine.Texture2D" />
<child AssetBundleName="889af9f557af4954a8271e4bd47669e6" AssetName="glow_00141" UnityType="UnityEngine.Material" />
<child AssetBundleName="c5b912f87af58c44789a0e72b9cb5716" AssetName="glow_00175" UnityType="UnityEngine.Material" />
<child AssetBundleName="abffe21d6aa8bd644ab62736766b4d1e" AssetName="glow_00141" UnityType="UnityEngine.Texture2D" />
<child AssetBundleName="8ee952aebc51efe42ad48cbc1fb60ae8" AssetName="glow_00175_3" UnityType="UnityEngine.Material" />
</resource>
AssetBundleName=GUID;
2. 开始打Assetbundle包
// 按顺序打包,1,font,2 sound,3 Texture,4.mat 5.fbx 其余的不管。prefab自动打包
List<EditorAssetConfig> texture2dList = new List<EditorAssetConfig>();
List<EditorAssetConfig> shaderList = new List<EditorAssetConfig>();
List<EditorAssetConfig> materialList = new List<EditorAssetConfig>();
List<EditorAssetConfig> otherList = new List<EditorAssetConfig>();
List<EditorAssetConfig> gameObjectList = new List<EditorAssetConfig>();
List<EditorAssetConfig> fontList = new List<EditorAssetConfig>();
List<EditorAssetConfig> audioClipList = new List<EditorAssetConfig>();
List<EditorAssetConfig> prefabtList = new List<EditorAssetConfig>();
EditorAssetConfig是一个结构体
public class EditorAssetConfig
{
// assetName = guid 形成好的
public string AssetBundleName;
// 资源类型
public string objType;
// 资源类型
public string AssetPath;
// sourcesName
public string AssetName;
// 是否是prefab
public bool isPrefab = false;
// 被引用的计数
public int dependenciesCount = 0;
// 下级(只有一维)因为只打包prefab
public List<EditorAssetConfig> child = new List<EditorAssetConfig> ();
}
BuildPipeline.PushAssetDependencies ();
// ===============
上面的子Asset先打包List循环打包
=================//
// ===============
BuildPipeline.PushAssetDependencies ();
打包Prefab
BuildPipeline.PopAssetDependencies();
================//
BuildPipeline.PopAssetDependencies();
// 注意事项,打出来的assetbundleName是 GUID生成的。运行过程中需要GUID来获取ab文件,所以打包过程中,需要把GUID对应原文件的名字记录下来。保存在xml中,相当于Unity5.x里德Mainfest 一样
xml version="1.0" encoding="UTF-8"?>
<root>
<resource AssetBundleName="cc5f40250fc1dad43a33c9b9b31001dd" AssetPath="Assets/ABResources/AllEffects/yujing_yuan_30.prefab" />
<resource AssetBundleName="38d758a33f8f4a147be81f1f80e647b4" AssetPath="Assets/Effects/Textures/Materials/glow_00143.mat" />
<resource AssetBundleName="ad78e334b10df0a4ea65f9ac040ee59a" AssetPath="Assets/Effects/Textures/glow_00143.png" />
// 打出来很多散的文件,
https://img-blog.csdn.net/20160919103408492
// 我们项目比较大,热更新上万个assetbundle io的操作太多。所以要合并文件
合并assetbundle的原理:
比如每30个assetbundle做成一个package
1.获取30个文件的byte
2.写入新的package.bytes文件里
3.这里需要一个头文件,表示记录了每一个assetbundle在这个package里的startIndex,跟EndIndex
=======================================================================================================
游戏运行过程中读取assetbundle
1.先下载package 。 根据自己记录MD5值验证是否更新package
2.下载package以后先解压。上面描述过。package的2进制头文件里写了一些描述,用来解压package用的
根据startIndex跟EndIndex 值 获取相应的单个Assetbundl文件,比如
// 解析头文件
MemoryStream stream = new MemoryStream (bytes);
long headLength = long.Parse (headLength(头文件长度));
stream.Position = stream.Length - headLength;
byte[] strBytes = new byte[headLength];
stream.Read (strBytes, 0, strBytes.Length);
string str = System.Text.UTF8Encoding.Default.GetString (strBytes);
// 字符串解析
string[] strArr = str.Split (';');
for (int i = 0; i < strArr.Length; i++) {
string _assetStr = strArr [i];
AssetBundlesInfo info = new AssetBundlesInfo ();
string[] assetInfo = _assetStr.Split ('|');
if (assetInfo.Length > 1) {
info.abName = assetInfo [0];
info.sindex = assetInfo [1];
info.length = assetInfo [2];
//解析完头文件内容自己定义一个结构里保存起来
package.childAssets.Add (info);
}
}
最后根据这个配置信息从package里扣出来每一个assetbundle文件,并且保存在缓存目录中
======================================= 热更新部分到这里 ==========================================================
======================================= 下面是内存管理 ==========================================================
加载一个assetbundle 以后会生成两个内存
AssetBundl m_assetBundle = www.bundle;
UnityEngine.Object m_Obj = www.bundle.MainAsset;
1是压缩的文件2.assetbundl.mainAsset获取的实例化的文件=你要Instance的prefab文件
我们加载一个内存以后习惯用Assetbunld.unload(false);
调用unload(false)以后assetbundle这个对象编程Null
但是m_Obj这个变量没有被释放,这个变量在内存管理中很重要
我们打包的是依赖关系。假设一个文理。被多个Object引用。那么这个m_Obj的refcount 引用计数会有N多个
当m_Obj的refcount 引用计数为0 的时候调用Resources.UnloadUnusedAssets(); 才能真正的从内存中移除
依赖关系的内存很复杂。假设有两个prefab A跟B 都依赖C这个文理
加载资源C,在加载A 实例化一个完整的A.prefab
然后立马释放掉
C.assetbundle.unload(false)
A.assetbundle.unload(false)
此事 C.assetbundl ,A.assetbundl 都变成空了。但是!
C.m_Obj, A.m_Obj 这两个Obj内存还存在。
这时候在加载B.prefab
由于B也依赖C资源所以先加载C资源
if(null != C.m_Obj) 说明这个依赖资源还在内存当中就不需要重新www.load了
直接加载 www.load(B)
从C#内存管理角度来说 C.m_Obj 什么时候会被释放掉呢?
Destroy(A)
Destroy(B)
按理说这时候C的引用计数变成0了。就可以释放掉了。
结果!C的引用计数还是1. 那是因为你自己写的类里面存储了这个对象类似
C.m_Obj = C的资源内存地址 这时候你自己的类引用了一个+1
那么C 不会被释放掉。
解决方法:
我们自己写的类里 不能直接“=”来引用
System.WeakReference 这个类是弱引用,C#的
m_Obj = new System.WeakReference (obj);
需要这么赋值,这时候C资源的内存管理完全由 系统来控制,当系统发现A,跟B都销毁了以后C的引用计数就变成0
这时候你在打印 m_Obj
Debug.Log(m_obj); ======Null