Asset Bundle的作用:
1.AssetBundle是一個(gè)壓縮包包含模型、貼圖、預(yù)制體、聲音、甚至整個(gè)場(chǎng)景,可以在游戲運(yùn)行的時(shí)候被加載;
2.AssetBundle自身保存著互相的依賴關(guān)系;
3.壓縮包可以使用LZMA和LZ4壓縮算法,減少包大小,更快的進(jìn)行網(wǎng)絡(luò)傳輸;
4.把一些可以下載內(nèi)容放在AssetBundle里面,可以減少安裝包的大??;
什么是AssetBundle
可以歸為兩點(diǎn):
1,它是一個(gè)存在于硬盤上的文件??梢苑Q之為壓縮包。這個(gè)壓縮包可以認(rèn)為是一個(gè)文件夾,里面包含了多個(gè)文件。這些文件可以分為兩類:serialized file 和 resource files。(序列化文件和源文件)
serialized file:資源被打碎放在一個(gè)對(duì)象中,最后統(tǒng)一被寫進(jìn)一個(gè)單獨(dú)的文件(只有一個(gè))
resource files:某些二進(jìn)制資源(圖片、聲音)被單獨(dú)保存,方便快速加載
2,它是一個(gè)AssetBundle對(duì)象,我們可以通過(guò)代碼從一個(gè)特定的壓縮包加載出來(lái)的對(duì)象。這個(gè)對(duì)象包含了所有我們當(dāng)初添加到這個(gè)壓縮包里面的內(nèi)容,我們可以通過(guò)這個(gè)對(duì)象加載出來(lái)使用。
Asset Bundle資源打包實(shí)例
無(wú)論是模型資源還是UI資源,最好是先把他們放在Prefab中,然后在做成Assetbundle。我們以模型來(lái)舉例,Assetbundle中可以放一個(gè)模型、也可以放多個(gè)模型,它是非常靈活了那么最需要考慮的就是模型空間占用的問(wèn)題。
下面我們來(lái)實(shí)際操作下,首先隨便先創(chuàng)建兩個(gè)3D對(duì)象,Cube和Capsule,并將他們做成Prefab,然后去指定資源的AssetBundle屬性,這里我將這兩個(gè)模型都打包成model.ab包
(xxxa/xxx)這里xxxa會(huì)生成目錄,名字為xxx ,后面的ab是后綴名,可自己制定。

設(shè)置好屬性后,下面開(kāi)始構(gòu)建AssetBundle包,首先先創(chuàng)建一個(gè)文件夾命名Editor,這個(gè)文件夾是不會(huì)進(jìn)行打包的特定編輯器擴(kuò)展文件夾。然后我們創(chuàng)建一個(gè)編輯器擴(kuò)展類CreateAssetbundles。寫入下面的代碼
using UnityEditor;
using System.IO;
public class CreateAssetbundles {
[MenuItem("AssetsBundle/Build AssetBundles")]
static void BuildAllAssetBundles()//進(jìn)行打包
{
string dir = "AssetBundles";
//判斷該目錄是否存在
if (Directory.Exists(dir) == false)
{
Directory.CreateDirectory(dir);//在工程下創(chuàng)建AssetBundles目錄
}
//參數(shù)一為打包到哪個(gè)路徑,參數(shù)二壓縮選項(xiàng) 參數(shù)三 平臺(tái)的目標(biāo)
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);
}
}
BuildAssetBundleOptions.None:使用LZMA算法壓縮,壓縮的包更小,但是加載時(shí)間更長(zhǎng)。使用之前需要整體解壓。一旦被解壓,這個(gè)包會(huì)使用LZ4重新壓縮。使用資源的時(shí)候不需要整體解壓。在下載的時(shí)候可以使用LZMA算法,一旦它被下載了之后,它會(huì)使用LZ4算法保存到本地上。
BuildAssetBundleOptions.UncompressedAssetBundle:不壓縮,包大,加載快
BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4壓縮,壓縮率沒(méi)有LZMA高,但是我們可以加載指定資源而不用解壓全部。
注意使用LZ4壓縮,可以獲得可以跟不壓縮想媲美的加載速度,而且比不壓縮文件要小。
然后回到Unity里面點(diǎn)擊我們剛剛擴(kuò)展出來(lái)的打包按鈕

點(diǎn)擊后我們的模型就打包了出來(lái),可以在工程的目錄下可以找到AssetBundles目錄,在AssetBundles下有個(gè)Scene文件夾里面就是我們的打包文件了,后綴是.ab

AssetBundle的加載
AssetBundle的加載有以下幾種方式,從內(nèi)存加載使用LoadFromMemoryAsync,從本地文件加載可以使用LoadFromFile,從服務(wù)器上Web上加載可以使用UnityWbRequest。下面我們來(lái)看看這幾種加載的方式。
首先可以先把我們Unity里面的兩個(gè)模型的Prefab Cube和Capsule刪除了,然后創(chuàng)建一個(gè)腳本掛在Camera上,打開(kāi)腳本
第一種加載方式(LoadFromMemoryAsync)從內(nèi)存加載
using UnityEngine;
using System.IO;
using System.Collections;
public class LoadFromFileExample : MonoBehaviour {
IEnumerator Start () {
string path = "AssetBundles/scene/model.ab";
//第一種加載AB的方式 LoadFromMemoryAsync
//異步加載
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return request;
AssetBundle ab = request.assetBundle;
//同步方式
//AssetBundle ab= AssetBundle.LoadFromMemory(File.ReadAllBytes(path));
//使用里面的資源
Object[] obj = ab.LoadAllAssets<GameObject>();//加載出來(lái)放入數(shù)組中
// 創(chuàng)建出來(lái)
foreach (Object o in obj)
{
Instantiate(o);
}
}
}
第二種方式(LoadFromFile)從本地加載
using UnityEngine;
using System.Collections;
public class LoadFromFileExample : MonoBehaviour {
IEnumerator Start () {
string path = "AssetBundles/scene/model.ab";
//第二種加載方式 LoadFromFile
//異步加載
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);
yield return request;
AssetBundle ab = request.assetBundle;
//同步加載
//AssetBundle ab = AssetBundle.LoadFromFile(path);
//使用里面的資源
Object[] obj = ab.LoadAllAssets<GameObject>();//加載出來(lái)放入數(shù)組中
// 創(chuàng)建出來(lái)
foreach (Object o in obj)
{
Instantiate(o);
}
}
}
第三種方式(UnityWbRequest)從服務(wù)器或者本地加載
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class LoadFromFileExample : MonoBehaviour {
IEnumerator Start () {
//第三種加載方式 使用UnityWbRequest 服務(wù)器加載使用http本地加載使用file
//string uri = @"file:///C:\Users\Administrator\Desktop\AssetBundleProject\AssetBundles\model.ab";
string uri = @"http://localhost/AssetBundles\model.ab";
UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri);
yield return request.Send();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
//使用里面的資源
Object[] obj = ab.LoadAllAssets<GameObject>();//加載出來(lái)放入數(shù)組中
// 創(chuàng)建出來(lái)
foreach (Object o in obj)
{
Instantiate(o);
}
}
}
這樣我們Model包里面的資源就加載出來(lái)并創(chuàng)建在場(chǎng)景里了。這時(shí)候運(yùn)行Unity就可以看到兩個(gè)模型都各自創(chuàng)建了出來(lái)

當(dāng)然也可以創(chuàng)建指定的資源例如
AssetBundle ab=AssetBundle.LoadFromFile("AssetBundles/scene/model.ab");
GameObject go = ab.LoadAsset<GameObject>("Cube");
Instantiate(go)
這樣就實(shí)現(xiàn)了Asset Bundle資源的加載了
AssetBundle分組策略
1,把經(jīng)常更新的資源放在一個(gè)單獨(dú)的包里面,跟不經(jīng)常更新的包分離
2,把需要同時(shí)加載的資源放在一個(gè)包里面
3,可以把其他包共享的資源放在一個(gè)單獨(dú)的包里面
4,把一些需要同時(shí)加載的小資源打包成一個(gè)包
5,如果對(duì)于一個(gè)同一個(gè)資源有兩個(gè)版本,可以考慮通過(guò)后綴來(lái)區(qū)分 v1 v2 v3 unity3dv1 unity3dv2
1,邏輯實(shí)體分組
a,一個(gè)UI界面或者所有UI界面一個(gè)包(這個(gè)界面里面的貼圖和布局信息一個(gè)包)
b,一個(gè)角色或者所有角色一個(gè)包(這個(gè)角色里面的模型和動(dòng)畫一個(gè)包)
c,所有的場(chǎng)景所共享的部分一個(gè)包(包括貼圖和模型)
2,按照類型分組
所有聲音資源打成一個(gè)包,所有shader打成一個(gè)包,所有模型打成一個(gè)包,所有材質(zhì)打成一個(gè)包
3,按照使用分組
把在某一時(shí)間內(nèi)使用的所有資源打成一個(gè)包??梢园凑贞P(guān)卡分,一個(gè)關(guān)卡所需要的所有資源包括角色、貼圖、聲音等打成一個(gè)包。也可以按照?qǐng)鼍胺?,一個(gè)場(chǎng)景所需要的資源一個(gè)包
依賴打包
意思就是例如有兩個(gè)模型使用的都是同一個(gè)材質(zhì)和貼圖,那么模型和材質(zhì)貼圖之間就是依賴關(guān)系。如果我們這兩個(gè)模型都單獨(dú)打包出來(lái)那么就會(huì)打包出兩份材質(zhì)和貼圖,這樣包就會(huì)變大,那么我們?nèi)绾谓鉀Q呢,這里Unity里面自帶有一種方式,那就是首先先把所依賴的材質(zhì)和貼圖單獨(dú)打包到一個(gè)文件夾中,然后再分別打包兩個(gè)需要依賴這個(gè)材質(zhì)和貼圖的模型。這樣Unity就會(huì)去查找這個(gè)材質(zhì)貼圖,發(fā)現(xiàn)這個(gè)材質(zhì)和貼圖已經(jīng)打包了出來(lái),那么它就不會(huì)去重復(fù)的打包材質(zhì)和貼圖了,這樣就大大減小了包的大小

上面一個(gè)是直接兩個(gè)模型分別打包出來(lái)可以看到材質(zhì)和貼圖都分別打包了出來(lái),分別都是63KB,而下面的是先將材質(zhì)和貼圖打包出來(lái)是62KB再將兩個(gè)2KB的模型打包出來(lái),總共也才64KB。

但是加載的時(shí)候模型、材質(zhì)和貼圖都要進(jìn)行去加載,不然就會(huì)出現(xiàn)財(cái)政的丟失
using UnityEngine;
public class LoadFromFileExample : MonoBehaviour {
void Start () {
AssetBundle ab = AssetBundle.LoadFromFile("AssetBundles/cube.ab");
AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/share.ab");
//GameObject go = ab.LoadAsset<GameObject>("Cube");
//Instantiate(go);
Object[] obj = ab.LoadAllAssets<GameObject>();//加載出來(lái)放入數(shù)組中
//創(chuàng)建出來(lái)
foreach (Object o in obj)
{
Instantiate(o);
}
}
}
使用AssetBundleManifest獲取所有的包
在打包AssetBundle后出現(xiàn)一個(gè)AssetBundles和一個(gè)AssetBundles.manifest兩個(gè)文件,打包出來(lái)的所有的AssetBundle包都會(huì)放在AssetBundles里面。


在Start方法里面寫入
AssetBundle manifesAB = AssetBundle.LoadFromFile("AssetBundles/AssetBundles");
AssetBundleManifest manifest= manifesAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
foreach (string name in manifest.GetAllAssetBundles())
{
print(name);
}
這時(shí)候運(yùn)行Unity就可以看到所有包都完整的輸出出來(lái)了。

利用Manifest加載某個(gè)包所依賴的包
AssetBundle manifesAB = AssetBundle.LoadFromFile("AssetBundles/AssetBundles");
AssetBundleManifest manifest= manifesAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
foreach (string name in manifest.GetAllAssetBundles())
{
print(name);
}
string []strs=manifest.GetAllDependencies("Cube.ab");
foreach (var name in strs)
{
AssetBundle.LoadFromFile("AssetBundles/"+name);
}
運(yùn)行后可以看到所依賴的Share包的資源也加載了出來(lái)

AssetBundle的卸載
卸載有兩個(gè)方面
1,減少內(nèi)存使用
2,有可能導(dǎo)致丟失
所以什么時(shí)候去卸載資源
AssetBundle.Unload(true)卸載所有資源,即使有資源被使用著
(1,在關(guān)卡切換、場(chǎng)景切換的時(shí)候
(2,資源沒(méi)被調(diào)用的時(shí)候
AssetBundle.Unload(false)卸載所有沒(méi)用被使用的資源
個(gè)別資源怎么卸載
(1,通過(guò) Resources.UnloadUnusedAssets.
(2,場(chǎng)景切換的時(shí)候
文件校驗(yàn)
文件校驗(yàn)可以在文件傳輸?shù)臅r(shí)候保證文件的完整性,例如A在給我傳輸了一個(gè)文件之前會(huì)生成一個(gè)校驗(yàn)碼,對(duì)于這個(gè)文件只會(huì)生成這一個(gè)唯一的校驗(yàn)碼,只要傳輸給我的文件有一點(diǎn)不一樣那么校驗(yàn)碼就會(huì)完全不同。所以A在傳輸給我文件的時(shí)候會(huì)把文件和校驗(yàn)碼都傳輸給我,當(dāng)我取到這個(gè)文件的時(shí)候我也會(huì)使用和A同樣一個(gè)算法去生成這個(gè)文件的校驗(yàn)碼,然后拿這個(gè)值和A傳輸給我的校驗(yàn)碼比對(duì),如果一樣說(shuō)明這個(gè)文件是完整的,如果不一樣那么就重新傳輸。下面是幾個(gè)算法生成的校驗(yàn)值
CRC MD5 SHA1
相同點(diǎn):
CRC、MD5、SHA1都是通過(guò)對(duì)數(shù)據(jù)進(jìn)行計(jì)算,來(lái)生成一個(gè)校驗(yàn)值,該校驗(yàn)值用來(lái)校驗(yàn)數(shù)據(jù)的完整性。
不同點(diǎn):
- 算法不同。CRC采用多項(xiàng)式除法,MD5和SHA1使用的是替換、輪轉(zhuǎn)等方法;
- 校驗(yàn)值的長(zhǎng)度不同。CRC校驗(yàn)位的長(zhǎng)度跟其多項(xiàng)式有關(guān)系,一般為16位或32位;MD5是16個(gè)字節(jié)(128位);SHA1是20個(gè)字節(jié)(160位);
- 校驗(yàn)值的稱呼不同。CRC一般叫做CRC值;MD5和SHA1一般叫做哈希值(Hash)或散列值;
- 安全性不同。這里的安全性是指檢錯(cuò)的能力,即數(shù)據(jù)的錯(cuò)誤能通過(guò)校驗(yàn)位檢測(cè)出來(lái)。CRC的安全性跟多項(xiàng)式有很大關(guān)系,相對(duì)于MD5和SHA1要弱很多;MD5的安全性很高,不過(guò)大概在04年的時(shí)候被山東大學(xué)的王小云破解了;SHA1的安全性最高。
- 效率不同,CRC的計(jì)算效率很高;MD5和SHA1比較慢。
- 用途不同。CRC一般用作通信數(shù)據(jù)的校驗(yàn);MD5和SHA1用于安全(Security)領(lǐng)域,比如文件校驗(yàn)、數(shù)字簽名等。
Unity Asset Bundle Browser tool
這是一個(gè)AssetBundle的查看工具,是Unity官方發(fā)布的一個(gè)擴(kuò)展工具,可以查看幫助打包AssetBundle和查看AssetBundle內(nèi)容??梢匀itHub上下載
https://github.com/Unity-Technologies/AssetBundles-Browser
下載后直接將里面的Editor擴(kuò)展工具拖入我們的Unity Project工程中



關(guān)于將AssetBundle上傳到服務(wù)器可以使用FTP上傳工具