【unity】Assetbundle實戰

http://www.xuanyusong.com/archives/2405html

上一篇文章中咱們相惜討論了Assetbundle的原理,若是對原理還不太瞭解的朋友能夠看這一篇文章:Unity3D研究院之Assetbundle的原理(六十一) 本篇文章咱們將說說assetbundle是如何實現的。android

1.建立Assetbundle數組

         不管是模型資源仍是UI資源,最好是先把他們放在Prefab中,而後在作成Assetbundle。咱們以模型來舉例,Assetbundle中能夠放一個模型、也能夠放多個模型,它是很是靈活了那麼最須要考慮的就是模型空間佔用的問題。緩存

好比咱們有兩個徹底同樣的模型,可是他們身上綁定的腳本不同,此時須要把這兩個模型放在兩個不一樣Prefab中。以下圖所示,咱們分別對這兩個Prefab打包,咱們能夠清晰的看到兩個相同的Prefab打包在一塊兒只佔1M空間,而將他們分別打包會佔1 + 1 = 2M空間。 Prefab在打包的同時會把模型身上的全部材質、貼圖、組件、腳本所有包含進去。服務器

wKioL1RInqXDewVeAAJfLEKzvz8352.jpg

         由此可得相同的模型儘可能打包在一塊兒,他們會公用一套資源文件。不相同的模型儘可能分開打包,相同模型具備不一樣的腳本、組件的話把他們放在不一樣的Prefab中,最後把這些Prefab一塊兒打包在一個Assetbundle中。以下圖所示,如今Project視圖中選擇須要打包的Prefab,而後在導航菜單欄中選擇Create Assetbundles Main表示分別打包、Create AssetBundles All表示將他們打包在一塊兒。異步

wKioL1RInqqQ9HClAALKw_vWDZg650.jpg

        這兩個prefab文件都指向了同一個模型,爲了讓它倆有所區別,我給它倆綁定了相同的腳本,可是腳本中的參數是不一樣的。在編輯器上給每一個Prefab賦值一個不一樣的名子,而後在Awake方法中進行輸出。編輯器

 

 

C#ide

1學習

2測試

3

4

5

6

7

8

9

10

11

12

13

using UnityEngine;

using System.Collections;

 

public class Script : MonoBehaviour

{

public string name;

 

void Awake ()

{

Debug.Log("my name is "+ name);

}

 

}

 

Create Assetbundles Main : 分開打包,會生成兩個Assetbundle。

 

 

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

[MenuItem("Custom Editor/Create AssetBunldes Main")]

static void CreateAssetBunldesMain ()

{

        //獲取在Project視圖中選擇的全部遊戲對象

Object[] SelectedAsset = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);

 

        //遍歷全部的遊戲對象

foreach (Object obj in SelectedAsset)

{

string sourcePath = AssetDatabase.GetAssetPath (obj);

//本地測試:建議最後將Assetbundle放在StreamingAssets文件夾下,若是沒有就建立一個,由於移動平臺下只能讀取這個路徑

//StreamingAssets是隻讀路徑,不能寫入

//服務器下載:就不須要放在這裏,服務器上客戶端用www類進行下載。

string targetPath = Application.dataPath + "/StreamingAssets/" + obj.name + ".assetbundle";

if (BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)) {

   Debug.Log(obj.name +"資源打包成功");

}

else

{

Debug.Log(obj.name +"資源打包失敗");

}

}

//刷新編輯器

AssetDatabase.Refresh ();

 

}

 

最核心的方法其實就它:

BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)

參數1:它只能放一個對象,由於咱們這裏是分別打包,因此經過循環將每一個對象分別放在了這裏。

參數2:能夠放入一個數組對象。

默認狀況下打的包只能在電腦上用,若是要在手機上用就要添加一個參數。

Android上:

BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies,BuildTarget.Android)

IOS上:

BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies,BuildTarget.iPhone)

另外,電腦上和手機上打出來的Assetbundle不能混用,不一樣平臺只能用本身的。

 

Create AssetBundles All:將全部對象打包在一個Assetbundle中。

 

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

[MenuItem("Custom Editor/Create AssetBunldes ALL")]

static void CreateAssetBunldesALL ()

{

 

Caching.CleanCache ();

 

string Path = Application.dataPath + "/StreamingAssets/ALL.assetbundle";

 

Object[] SelectedAsset = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);

 

foreach (Object obj in SelectedAsset)

{

Debug.Log ("Create AssetBunldes name :" + obj);

}

 

//這裏注意第二個參數就行

if (BuildPipeline.BuildAssetBundle (null, SelectedAsset, Path, BuildAssetBundleOptions.CollectDependencies)) {

AssetDatabase.Refresh ();

} else {

 

}

}

 兩次打包完畢後,在StreamingAssets文件夾中就看到了這三個assetbundle文件。

 

wKioL1RInqHg8KZMAABW2vAQsGQ856.jpg

2.讀取Assetbundle 

       而後咱們來學習如何運行時讀取Assetbundle,Assetbundle是能夠同時放在服務器或者本地的,不管放在哪裏兩種下載讀取的方式是徹底同樣的。因此我建議在作unity項目的時候開始就把資源放在Assetbundle中在本地來作,等作的差很少了直接把Assetbundle放在服務器上,由於兩種讀取的方式徹底同樣,這樣之後更新資源會方便不少。而後是讀取,而且加載到遊戲中。

 

 

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

using UnityEngine;

using System.Collections;

 

public class RunScript : MonoBehaviour

{

 

    //不一樣平臺下StreamingAssets的路徑是不一樣的,這裏須要注意一下。

    public static readonly string PathURL =

#if UNITY_ANDROID

"jar:file://" + Application.dataPath + "!/assets/";

#elif UNITY_IPHONE

Application.dataPath + "/Raw/";

#elif UNITY_STANDALONE_WIN || UNITY_EDITOR

"file://" + Application.dataPath + "/StreamingAssets/";

#else

        string.Empty;

#endif

 

void OnGUI()

{

if(GUILayout.Button("Main Assetbundle"))

{

StartCoroutine(LoadMainGameObject(PathURL + "Prefab0.assetbundle"));

StartCoroutine(LoadMainGameObject(PathURL +  "Prefab1.assetbundle"));

}

 

if(GUILayout.Button("ALL Assetbundle"))

{

StartCoroutine(LoadALLGameObject(PathURL + "ALL.assetbundle"));

}

 

}

 

//讀取一個資源

 

private IEnumerator LoadMainGameObject(string path)

{

WWW bundle = new WWW(path);

 

yield return bundle;

 

//加載到遊戲中

yield return Instantiate(bundle.assetBundle.mainAsset);

 

bundle.assetBundle.Unload(false);

}

 

//讀取所有資源

 

private IEnumerator LoadALLGameObject(string path)

{

WWW bundle = new WWW(path);

 

yield return bundle;

 

//經過Prefab的名稱把他們都讀取出來

Object  obj0 =  bundle.assetBundle.Load("Prefab0");

Object  obj1 =  bundle.assetBundle.Load("Prefab1");

 

//加載到遊戲中

yield return Instantiate(obj0);

yield return Instantiate(obj1);

bundle.assetBundle.Unload(false);

}

 

}

 這裏咱們詳細的說說 下載類WWW

WWW bundle = new WWW(path);

這樣的作法是經過一個路徑進行下載(不管是服務器路徑仍是本地路徑下載操做都同樣)可是bundle只能保存在內存中,也就是退出遊戲在進入還得從新下,很顯然在遊戲中咱們不能使用這種方式。

 

 

C#

1

2

3

4

5

6

7

8

9

10

11

private IEnumerator LoadMainCacheGameObject(string path)

{

WWW bundle = WWW.LoadFromCacheOrDownload(path,5);

 

yield return bundle;

 

//加載到遊戲中

yield return Instantiate(bundle.assetBundle.mainAsset);

 

bundle.assetBundle.Unload(false);

}

 

使用的方法是WWW.LoadFromCacheOrDownload(path,5);

參數1:服務器或者本地下載地址

參數2:版本號

         Unity會下載Assetbundle本地中,它的工做原理是先經過(版本號和下載地址)先在本地去找看有沒有這個Assetbundle,若是有直接返回對象,若是沒有的話,在根據這個下載地址從新從服務器或者本地下載。這裏版本號起到了很重要的做用,舉個例子,同一下載地址版本號爲1的時候已經下載到本地,此時將版本號的參數改爲2 那麼它又會從新下載,若是還保持版本號爲1那麼它會從本地讀取,由於本地已經有版本號爲1的這個Assetbundle了。你不用擔憂你的資源本地下載過多,也不用本身手動刪除他們,這一切的一切Unity會幫咱們自動完成,它會自動刪除掉下載後最不經常使用的Assetbundle ,若是下次須要使用的話只要提供下載地址和版本後它會從新下載。

        咱們在聊聊Assetbundle 中的腳本,在移動平臺下Assetbundle裏面放的腳本是不會被執行的,還記得咱們打包前給兩個Prefab掛上了腳本嗎?在手機上將Assetbundle下載到本地後,加載進遊戲中Prefab會自動在本地找它身上掛着的腳本,他是根據腳本的名來尋找,若是本地有這條腳本的話,Prefab會把這個腳本從新綁定在自身,而且會把打包前的參數傳遞進來。若是本地沒有,身上掛的條腳本永遠都不會被執行。

      在Prefab打包前,我在編輯器上給腳本中的變量 name 賦了不一樣值,當Prefab從新載入遊戲的時候,它身上腳本的參數也會從新輸出。

wKiom1RInliSehhKAADpYUHO_e8966.jpg

若是你的Assetbundle中的Prefab上引用的對象,那麼這樣作就會出錯了,你須要設定他們的依賴關係。或者運行時經過腳本動態的載入對象。

 

http://docs.unity3d.com/Documentation/ScriptReference/BuildPipeline.PopAssetDependencies.html

 

http://docs.unity3d.com/Documentation/ScriptReference/BuildPipeline.PushAssetDependencies.html

像這樣從新打包就能夠。

 

3.打包場景

     上面咱們說過了打包Prefab,其實咱們還能夠把整個場景進行打包,由於移動平臺不能更新腳本,因此這個功能就會有所限制,個人建議是烘培場景、而後把多個場景可複用的對象移除,場景中只保留獨一無二的遊戲對象,而後在打包場景,運行遊戲時載入場景後,在動態的將以前移除的對象從新添加進來。

能夠參考 : Unity3D研究院之將場景導出XML或JSON或二進制而且解析還原場景(四十二)

 

 

C#

1

2

3

4

5

6

7

8

9

10

11

[MenuItem("Custom Editor/Create Scene")]

static void CreateSceneALL ()

{

//清空一下緩存

Caching.CleanCache();

string Path = Application.dataPath + "/MyScene.unity3d";

string  []levels = {"Assets/Level.unity"};

     //打包場景

     BuildPipeline.BuildPlayer( levels, Path,BuildTarget.WebPlayer, BuildOptions.BuildAdditionalStreamedScenes);

AssetDatabase.Refresh ();

}

          不一樣平臺下須要選擇  BuildTarget.Android 和 BuildTarget.iPhone 。 切記這段代碼是把Level.unity常見文件打包到MyScene.unity3d文件中,因此在解包的時候也應當是先解開MyScene.unity3d,而後在去加載Level.unity場景,無需在ProjectSetting中註冊新場景。

 

C#

1

2

3

4

5

6

7

private IEnumerator LoadScene()

{

WWW download = WWW.LoadFromCacheOrDownload ("file://"+Application.dataPath + "/MyScene.unity3d", 1);

  yield return download;

  var bundle = download.assetBundle;

     Application.LoadLevel ("Level");

}

 

          在測試狀況下你可能會頻繁的打包生成Assetbundle,若是忘記改版本號的話可能會讀取以前的緩存,可能就會看不到新的效果,因此我建議在bunild Assetbundle的時候強制清空一下緩存。

Caching.CleanCache();

最後點擊按鈕進行加載Assetbundle和 Scene吧。

wKiom1RInliz6au4AADO3hf134g171.jpg

最後是下載地址:http://vdisk.weibo.com/s/Hrvea

歡迎你們一塊兒學習,歡迎給我留言、歡迎一塊兒討論,祝你們學習愉快 啦啦啦啦。

 

2014年10月補充

WWW.LoadFromCacheOrDownload 這個方法建議你們之後不要再用了

由於是異步方法,並且還佔用內存。

強烈建議使用AssetBundle.CreatFromFile 它是一個同步方法,不佔用內存,由於不用把assetbundle預先載入內存,而直接從文件裏讀取,相似Resouces.Load的方式。如今IOS 和 android 都支持了,強烈建議用。

打包的時候須要選擇不壓縮。

 

1

2

3

4

//打包場景

BuildPipeline.BuildStreamedSceneAssetBundle(levels, path, target, BuildOptions.UncompressedAssetBundle))

//打包資源

BuildPipeline.BuildAssetBundle(null, assets, path, BuildAssetBundleOptions.UncompressedAssetBundle | BuildAssetBundleOptions.CollectDependencies, target);

由於不壓縮, 因此就須要咱們本身來壓縮資源了, 能夠用LZMA 和 GZIP來進行壓縮。

1.打包出來的Assetbundle咱們本身用LZMA壓縮,上傳到服務器上。

2.IOS或者Android下載這些assetbundle

3.解壓縮這些assetbundle而且保存在Application.persistentDataPath 目錄下。

4.之後經過AssetBundle.CreatFromFile  讀取assetbundle。

此法確實可行,咱們已經在實際項目中轟轟烈烈的使用了。。

相關文章
相關標籤/搜索