前言:下面文字適用于對(duì)AssetBundle有一點(diǎn)了解的朋友逊拍,閱讀大約10分鐘上鞠,AssetBundle基本概念等知識(shí)可以網(wǎng)上找一下。
1芯丧、打包API的變化
Unity5.x中芍阎,AssetBundle相關(guān)的API做了極大的簡(jiǎn)化,合并了多個(gè)情況的函數(shù)缨恒,合并了資源和場(chǎng)景打包函數(shù)谴咸。
1、資源
- unity4.x :
BuildAssetBundle骗露,BuildAssetBundleExplicitAssetNames寿冕。當(dāng)然不止兩個(gè)函數(shù),共有10個(gè)左右的重載函數(shù)椒袍,
public static bool BuildAssetBundle(UnityEngine.Object mainAsset, UnityEngine.Object[] assets, string pathName, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);
mainAssets:指定mainAsset,這樣解析該AB包的時(shí)候可以通過(guò)assetBundle.mainAsset得到
assets:指定打在一起的Asset驼唱,解析時(shí)可通過(guò)LoadAsset(“name”)得到
pathName:打包生成的存儲(chǔ)路徑
assetBundleOptions:打包選項(xiàng)
BuildTarget:打包目標(biāo)平臺(tái)
BuildAssetBundleExplicitAssetNames增加對(duì)AssetBundle命名,在4.x中驹暑,資源打包后的名字就是主資源的名字玫恳。
- unity5.x :
首先打包資源的設(shè)置方式有所變化,直接在編輯器中選中要打包的資源优俘,在Inspector視圖下就出現(xiàn)如下選項(xiàng)
bundle名字可以在中Editor設(shè)置京办;也可以通過(guò)代碼設(shè)置,需要用AssetBundleBuild包裝起來(lái)帆焕。
簡(jiǎn)單說(shuō)就是惭婿,只要給你要打包的資源設(shè)置一個(gè)AssetBundleName,在打包的時(shí)候Unity就是自動(dòng)對(duì)這些設(shè)置了名字的資源進(jìn)行打包叶雹,名字相同的打成一個(gè)bundle财饥,并且自動(dòng)處理資源間的依賴關(guān)系,最后生成每個(gè)AssetBundle文件和對(duì)應(yīng)一個(gè)AssetBundleManifest文件折晦,AssetBundleManifest中記錄的就是該bundle的依賴等信息钥星。
使用BuildPipeline.BuildAssetBundles對(duì)游戲資源打包:API地址
public static AssetBundleManifest BuildAssetBundles(string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);
public static AssetBundleManifest BuildAssetBundles(string outputPath, AssetBundleBuild[] builds, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);
outputPath : 生成到目錄
AssetBundleBuild:對(duì)要打包的資源的封裝的數(shù)組
該結(jié)構(gòu)有三個(gè)屬性,assetBundleName(設(shè)置AssetBundle的名字)满着、assetBundleVariant(可以理解為二級(jí)名字)谦炒、assetNames(數(shù)組)
Variant參數(shù):
在Inspector界面最下方贯莺,除了可以設(shè)置AssetBundle的名字,后面還可以指定Variant參數(shù)宁改。打包時(shí)缕探,Variant會(huì)作為后綴添加在Bundle名字之后,相同的AssetBundle Name还蹲,不同的Variant Name撕蔼,能夠讓AssetBundle方便地進(jìn)行“多分辨率支持”。
BuildAssetBundleOptions : 打包選項(xiàng)(后面詳細(xì)說(shuō))
BuildTarget : 打包的目標(biāo)平臺(tái)秽誊,Android or iOS 鲸沮,and many
參數(shù)基本和unity4.x類(lèi)似,只是資源的表示方式上不同锅论。
2讼溺、場(chǎng)景
- unity4.x : BuildStreamedSceneAssetBundle
public static string BuildStreamedSceneAssetBundle(string[] levels, string locationPath, BuildTarget target, BuildOptions options);
- unity5.x:場(chǎng)景和資源共用一個(gè)打包函數(shù),對(duì)普通資源的設(shè)置AssetBundle方式最易,同樣可以對(duì)場(chǎng)景資源使用怒坯,也就是說(shuō),5.x中所有打包AssetBundle的資源都用一個(gè)接口了藻懒。
3剔猿、選項(xiàng)
BuildAssetBundleOptions:AssetBundle的打包策略,可以根據(jù)需求設(shè)置最合適的打包策略嬉荆。
- None:使用默認(rèn)打包方式(unity4.x中是LZMA壓縮归敬、不完備?不收集依賴鄙早?不生成唯一ID汪茧;unity5.x是LZMA壓縮、資源完備限番、收集依賴舱污、唯一ID)
- UncompressedAssetBundle:打包AssetBundle不進(jìn)行壓縮;
- CompleteAssets:用于保證資源的完備性(把該資源和它所有依賴打包到一個(gè)AssetBundle中)弥虐,unity5.x默認(rèn)開(kāi)啟扩灯;
- CollectDependencies:用于收集資源的依賴項(xiàng)(在打包的時(shí)候,會(huì)不會(huì)去找到依賴是否已經(jīng)打成了AssetBundle霜瘪,只把沒(méi)有生成AssetBundle的依賴打進(jìn)包內(nèi))unity5.x默認(rèn)開(kāi)啟珠插;
- DeterministicAssetBundle:為資源維護(hù)固定ID(唯一標(biāo)志AssetBundle,主要用來(lái)增量打包)粥庄,unity5.x默認(rèn)開(kāi)啟丧失;
Unity5.x新增
- ForceRebuildAssetBundle:用于強(qiáng)制重打所有AssetBundle文件豺妓;
- IgnoreTypeTreeChanges:判斷AssetBundle更新時(shí)惜互,是否忽略TypeTree的變化布讹;
- DisableWriteTypeTree:不包含TypeTree類(lèi)型信息(影響資源版本變化,可以讓AssetBundle更小训堆,加載更快描验;與4.x不同的是,對(duì)于移動(dòng)平臺(tái)坑鱼,5.x下默認(rèn)會(huì)將TypeTree信息寫(xiě)入AssetBundle)膘流;
- AppendHashToAssetBundleName:用于將Hash值添加在AssetBundle文件名之后,開(kāi)啟這個(gè)選項(xiàng) 可以直接通過(guò)文件名來(lái)判斷哪些Bundle的內(nèi)容進(jìn)行了更新(4.x下普遍需要通過(guò)比較二進(jìn)制等方法來(lái)判斷鲁沥,但在某些情況下即使內(nèi)容不變重新打包呼股,Bundle的二進(jìn)制也會(huì)變化);
- ChunkBasedCompression:用于使用LZ4格式進(jìn)行壓縮画恰,5.3新增,默認(rèn)壓縮格式為L(zhǎng)ZMA;
LZMA(Ziv-Markov chain algorithm)格式
Unity打包成AssetBundle時(shí)的默認(rèn)格式洞坑,會(huì)將序列化數(shù)據(jù)壓縮成LZMA流呀页,使用時(shí)需要整體解包。優(yōu)點(diǎn)是打包后體積小考润,缺點(diǎn)是解包時(shí)間長(zhǎng)狭园,且占用內(nèi)存。
LZ4格式
5.3新版本添加的壓縮格式糊治,壓縮率不及LZMA唱矛,但是不需要整體解壓。LZ4是基于chunk的算法井辜,加載對(duì)象時(shí)只有響應(yīng)的chunk會(huì)被解壓揖赴。
- StrictMode:任何錯(cuò)誤都將導(dǎo)致打包失敗抑胎;
2燥滑、打包過(guò)程
1、資源
unity4.x:下面代碼是將某個(gè)文件中的材質(zhì)打包阿逃,GetDependencies可以將直接和間接的依賴都找到铭拧。
public void BuildABundle()
{
string[] assets = AssetDatabase.FindAssets("t:mat", new string[] { "Assets/Material" });
string[] deps = AssetDatabase.GetDependencies(assets);
for (int i = 0; i < deps.Length; ++i)
{
Object ast = AssetDatabase.LoadAssetAtPath(deps[i], typeof(Object));
BuildPipeline.BuildAssetBundle(ast, new Object[] { }, "Assets/AssetBundles/Material"
, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
, BuildTarget.StandaloneWindows64);
}
}
unity5.x:
- 如果在Editor下設(shè)置了AssetBundle的Name,一句話就搞定
public void BuildABundle()
{
BuildPipeline.BuildAssetBundles(Application.dataPath + "/AssetBundles"
, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
}
- 也可以通過(guò)代碼指定
public void BuildABundle()
{
List<AssetBundleBuild> wrap = new List<AssetBundleBuild>();
string[] assets = AssetDatabase.FindAssets("t:mat", new string[] { "Assets/Material" });
string[] deps = AssetDatabase.GetDependencies(assets);
string path = "";
AssetBundleBuild build;
for (int i = 0; i < deps.Length; ++i)
{
path = deps[i];
build = new AssetBundleBuild();
build.assetBundleName = this.GetFileName(path) + "_mat.assetBundle";
//build.assetBundleVariant
build.assetNames = new string[] { path };
wrap.Add(build);
}
BuildPipeline.BuildAssetBundles("", wrap.ToArray(), BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
public string GetFileName(string inputPath)
{
int index = inputPath.LastIndexOf('\\');
if (index < 0)
{
index = inputPath.LastIndexOf('/');
}
int start = index + 1;
start = (start < 0 ? 0 : start);
int end = inputPath.LastIndexOf('.');
end = (end < 0 ? inputPath.Length : end);
return inputPath.Substring(start, end - start);
}
需要注意:Unity會(huì)將AssetBundle名字設(shè)置為相同的資源打包在一個(gè)bundle里面恃锉。
2搀菩、場(chǎng)景
unity4.x:
string[] assets = AssetDatabase.FindAssets("t:unity", new string[] { "Assets/Scene" });
BuildPipeline.BuildStreamedSceneAssetBundle(assets, "Assets/AssetBundles/Scene", BuildTarget.StandaloneWindows64);
unity5.x:和打包普通資源一樣。
以上的代碼只是作為例子破托,實(shí)際打包時(shí)考慮的東西有很多肪跋。
3、依賴打包
unity4.x:不是一般的麻煩土砂,需要自己去建立依賴關(guān)系州既,并且自己維護(hù)依賴數(shù)據(jù)谜洽。提供了兩個(gè)API,分別是BuildPipeline.PushAssetDependencies和BuildPipeline.PopAssetDependencies∥庖叮現(xiàn)在已經(jīng)很少用阐虚,所以簡(jiǎn)單寫(xiě)一下
public void BuildABundle()
{
string[] assets = AssetDatabase.FindAssets("t:unity", new string[] { "Assets/Scene" });
Object ast = null;
string[] deps = null;
for (int i = 0; i < assets.Length; ++i)
{
deps = AssetDatabase.GetDependencies(new string[] { assets[i] });
BuildPipeline.PushAssetDependencies();
for (int j = 0; j < deps.Length; ++j)
{
if (assets[i] != deps[j])
{
ast = AssetDatabase.LoadAssetAtPath(deps[j], typeof(Object));
BuildPipeline.BuildAssetBundle(ast, new Object[] { }, "Assets/AssetBundles/Scene"
, BuildAssetBundleOptions.None //打包策略需要仔細(xì)考慮,這里隨便寫(xiě)的一個(gè)
, BuildTarget.StandaloneWindows64);
}
}
BuildPipeline.PopAssetDependencies();
ast = AssetDatabase.LoadAssetAtPath(assets[i], typeof(Object));
BuildPipeline.BuildAssetBundle(ast, new Object[] { }, "Assets/AssetBundles/Scene"
, BuildAssetBundleOptions.None //打包策略需要仔細(xì)考慮蚌卤,這里隨便寫(xiě)的一個(gè)
, BuildTarget.StandaloneWindows64);
}
}
例子只是演示怎么用实束,真正寫(xiě)的時(shí)候會(huì)有一個(gè)遞歸的過(guò)程。
一個(gè)Push對(duì)應(yīng)一個(gè)Pop逊彭,打包當(dāng)前資源的時(shí)候咸灿,會(huì)把當(dāng)前棧中的依賴做為依賴添加到資源bundle中。還得保證在打包某個(gè)資源之前侮叮,它的依賴已經(jīng)打包好了析显,不然就會(huì)出現(xiàn)丟各種東西。
unity5.x:現(xiàn)在根本不用自己依賴打包签赃,因?yàn)閡nity已經(jīng)自動(dòng)做了谷异,所以代碼和之前的一樣。
4锦聊、依賴管理
unity4.x:自己把依賴關(guān)系記錄下來(lái)歹嘹,以便在加載的時(shí)候可以知道先加載的依賴,這樣才能正確的加載資源孔庭,具體可查看這篇博客尺上。
unity5.x:在打Bundle的時(shí)候,同時(shí)會(huì)為每個(gè)bundle生成一個(gè)配置文件(.manifest文件
)圆到,里面記錄著bundle的信息
ManifestFileVersion: 0
CRC: 2829116721
Hashes:
AssetFileHash:
serializedVersion: 2
Hash: 2e559c427b01f4b1438782d05c4d59f2
TypeTreeHash:
serializedVersion: 2
Hash: 87623bb9f607f4edb72c2338fde167fc
HashAppended: 0
ClassTypes:
- Class: 1
Script: {instanceID: 0}
- Class: 4
Script: {instanceID: 0}
- Class: 21
Script: {instanceID: 0}
- Class: 23
Script: {instanceID: 0}
- Class: 33
Script: {instanceID: 0}
- Class: 43
Script: {instanceID: 0}
- Class: 65
Script: {instanceID: 0}
Assets:
- Assets/Cube.prefab
Dependencies:
- F:/Test/Assets/AssetBundles/mat.sd
版本號(hào)
CRC
Asset File的Hash Code怎抛,全局唯一ID
Type Tree的Hash Code,全局唯一ID芽淡,AssetBundle所包含的所有類(lèi)型(目前不了解)
Class types:AssetBundle包含的所有"類(lèi)型"(可以參看unity序列化)
Asset names:包含的資源
Dependent AssetBundle names:資源的所有依賴
在unity doc中詳細(xì)的講了里面有哪些信息马绝,詳情請(qǐng)點(diǎn)擊這里
目前只是在做增量打包的時(shí)候會(huì)用到,但打包過(guò)程中完全不用管理依賴和manifest文件挣菲,因?yàn)闊o(wú)論是依賴還是增量富稻,unity都已經(jīng)做好了,在加載資源的時(shí)候只要調(diào)用統(tǒng)一的接口可以取到依賴白胀,并且已經(jīng)按照“正確的順序”了椭赋,只需循環(huán)數(shù)組加載。
5或杠、AssetBundle加載
unity4.x和unity5.x的資源加載方式并沒(méi)有多大變化哪怔,API上沒(méi)有變化,只是在依賴加載的時(shí)候,unity5.x實(shí)在太方便认境,既不需要自己做依賴關(guān)系胚委,也不需要遞歸去加載。
先回顧一下API:
- 1元暴、WWW加載
string netpath = "http://www.ab.com/down/test.assetBundle";//可以從網(wǎng)絡(luò)上下載
string path = Application.persistentDataPath + "/ab";//也可以是本地路徑
int versionCode = 1; //版本號(hào)
private IEnumerator _LoadAB()
{
WWW www = new WWW(path);
yield return www;
AssetBundle ab = www.assetBundle;
Object a = ab.LoadAsset("asset");
GameObject.Instantiate(a);
www.Dispose();
www = null;
ab.Unload(false);
}
private IEnumerator _LoadABorCache()
{
//先檢查本地是否有緩存資源篷扩,沒(méi)有再?gòu)木W(wǎng)絡(luò)獲取兄猩,并且獲取后進(jìn)行緩存茉盏。
WWW www = WWW.LoadFromCacheOrDownload(netpath, versionCode);
yield return www;
AssetBundle ab = www.assetBundle;
Object a = ab.LoadAsset("asset");
GameObject.Instantiate(a);
www.Dispose();//釋放掉WWW資源
www = null;
ab.Unload(false);
}
- 2、AssetBundle加載
unity4.x :
AssetBundle.CreateFromFile
AssetBundle.CreateFromMemory
AssetBundle.CreateFromMemoryImmediate
unity5.x :
AssetBundle.LoadFromFile
AssetBundle.LoadFromFileAsync
AssetBundle.LoadFromMemory
AssetBundle.LoadFromMemoryAsync
需要注意的是枢冤,unity5.x中鸠姨,LoadFromFile和LoadFromFileAsync已經(jīng)支持直接加載壓縮文件了。并且新機(jī)制打包無(wú)法指定Assetbundle.mainAsset淹真,因此無(wú)法再通過(guò)mainAsset來(lái)直接獲取資源
開(kāi)啟DisableWriteTypeTree可能造成AssetBundle對(duì)Unity版本的兼容問(wèn)題讶迁,但會(huì)使Bundle更小,同時(shí)也會(huì)略微提高加載速度核蘸。
- unity5.x加載代碼
void Start () {
AssetBundle manifestAb = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundles");
AssetBundleManifest manifest = manifestAb.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
string[] deps = manifest.GetAllDependencies("Cube");
List<AssetBundle> depList = new List<AssetBundle>();
for (int i = 0; i < deps.Length; ++i)
{
AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + deps[i]);
depList.Add(ab);
}
AssetBundle cubeAb = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/Cube");
Object org = cubeAb.LoadAsset("Cube");
Instantiate(org);
cubeAb.Unload(false);
for (int i = 0; i < depList.Count; ++i)
{
depList[i].Unload(false);
}
manifestAb.Unload(true);
}
unity4.x : 會(huì)把文件整個(gè)生成內(nèi)存鏡像
AssetBundle加載以后巍糯,在內(nèi)存中只是一塊內(nèi)存數(shù)據(jù),沒(méi)有特定的結(jié)構(gòu)客扎,內(nèi)存包含了webstream祟峦、壓縮數(shù)據(jù)(如果是壓縮資源就包含)、解壓buffer徙鱼、解壓后的數(shù)據(jù)宅楞。需要使用www.Dispose釋放掉webstream的內(nèi)存。
unity5.3+:最新的加載方式袱吆,只會(huì)載入header厌衙,在真正加載Asset的時(shí)候,通過(guò)這些header到文件中去取相應(yīng)的數(shù)據(jù)绞绒。詳情
5婶希、資源加載
AssetBundle加載到內(nèi)存中以后,還需要調(diào)用一些Load函數(shù)蓬衡,把Asset加載出來(lái)饲趋,第一次Load的時(shí)候,會(huì)比較慢撤蟆,unity會(huì)根據(jù)Asset的結(jié)構(gòu)到原始數(shù)據(jù)中去找對(duì)應(yīng)的內(nèi)存奕塑,取到數(shù)據(jù)并且生成相應(yīng)的結(jié)構(gòu),這樣內(nèi)存中就又多了一些Asset結(jié)構(gòu)家肯。
unity4.x :
AssetBundle.Load
AssetBundle.LoadAsync
AssetBundle.LoadAll
unity5.x :
AssetBundle.LoadAsset
AssetBundle.LoadAssetAsync
AssetBundle.LoadAllAssets
AssetBundle.LoadAllAssetsAsync
5龄砰、資源卸載
AssetBundle.Unload(flase)是釋放AssetBundle文件的內(nèi)存鏡像,不包含Load創(chuàng)建的Asset內(nèi)存對(duì)象。
AssetBundle.Unload(true)是釋放那個(gè)AssetBundle文件內(nèi)存鏡像和并銷(xiāo)毀所有通過(guò)該bundle創(chuàng)建的Asset內(nèi)存對(duì)象换棚。
有些沒(méi)有引用的“游離”的資源式镐,沒(méi)有API直接卸載它的內(nèi)存鏡像,只有調(diào)用Resources.UnloadUnusedAssets固蚤,這個(gè)函數(shù)很耗時(shí)娘汞,它會(huì)遍歷所有內(nèi)存中的資源,找出并釋放掉沒(méi)有使用的資源夕玩。還可以用Resources.UnloadAsset(Object assetToUnload)你弦,卸載單個(gè)確定的“資源”,注意不是GameObject燎孟,因?yàn)楹苈闊┱夷硞€(gè)GameObject所用的資源(Texture禽作、Mesh、Anim揩页、Audio)旷偿,所以一般不會(huì)用它。