天天看點

Unity3D研究院之Assetbundle的實戰

Unity3D研究院之Assetbundle的實戰

上一篇文章中我們相惜讨論了Assetbundle的原理,如果對原理還不太了解的朋友可以看這一篇文章:Unity3D研究院之Assetbundle的原理(六十一)本篇文章我們将說說assetbundle是如何實作的。

1.建立Assetbundle

         無論是模型資源還是UI資源,最好是先把他們放在Prefab中,然後在做成Assetbundle。我們以模型來舉例,Assetbundle中可以放一個模型、也可以放多個模型,它是非常靈活了那麼最需要考慮的就是模型空間占用的問題。

比如我們有兩個完全一樣的模型,但是他們身上綁定的腳本不一樣,此時需要把這兩個模型放在兩個不同Prefab中。如下圖所示,我們分别對這兩個Prefab打包,我們可以清晰的看到兩個相同的Prefab打包在一起隻占1M空間,而将他們分别打包會占1 + 1 = 2M空間。 Prefab在打包的同時會把模型身上的所有材質、貼圖、元件、腳本全部包含進去。

Unity3D研究院之Assetbundle的實戰

2B963149-6D2C-4540-8709-230471E52D02.jpg (72.17 KB, 下載下傳次數: 0)

下載下傳附件  儲存到相冊

2014-8-19 16:21 上傳

         由此可得相同的模型盡量打包在一起,他們會公用一套資源檔案。不相同的模型盡量分開打包,相同模型具有不同的腳本、元件的話把他們放在不同的Prefab中,最後把這些Prefab一起打包在一個Assetbundle中。如下圖所示,現在Project視圖中選擇需要打包的Prefab,然後在導航菜單欄中選擇Create Assetbundles Main表示分别打包、Create AssetBundles All表示将他們打包在一起。

Unity3D研究院之Assetbundle的實戰

螢幕快照-2013-06-26-下午2.28.20.png (108.1 KB, 下載下傳次數: 0)

下載下傳附件  儲存到相冊

2014-8-19 16:21 上傳

        這兩個prefab檔案都指向了同一個模型,為了讓它倆有所差別,我給它倆綁定了相同的腳本,但是腳本中的參數是不同的。在編輯器上給每個Prefab指派一個不同的名子,然後在Awake方法中進行輸出。

[AppleScript] 純文字檢視 複制代碼 ?

  01 02 03 04 05 06 07 08 09 10 11 12

using

UnityEngine;

using

System.Collections;

public

class

Script

:

MonoBehaviour

{

public

string

name

;

void Awake

(

)

{

Debug.Log

(

"my name is "

+

name

)

;

}

}

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

[AppleScript] 純文字檢視 複制代碼 ?

  01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

[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:可以放入一個數組對象。

預設情況下打的包隻能在電腦上用,如果要在手機上用就要添加一個參數。

上:

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

IOS上:

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

另外,電腦上和手機上打出來的Assetbundle不能混用,不同平台隻能用自己的。

Create AssetBundles All:将所有對象打包在一個Assetbundle中。

[AppleScript] 純文字檢視 複制代碼 ?

  01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21

<

font

face

=

"Verdana,"

>

[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

{

}

}

<

/

font

>

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

Unity3D研究院之Assetbundle的實戰

螢幕快照-2013-06-26-下午2.47.05.png (17.59 KB, 下載下傳次數: 0)

下載下傳附件  儲存到相冊

2014-8-19 16:25 上傳

2.讀取Assetbundle

        然後我們來學習如何運作時讀取Assetbundle,Assetbundle是可以同時放在伺服器或者本地的,無論放在哪裡兩種下載下傳讀取的方式是完全一樣的。是以我建議在做unity項目的時候開始就把資源放在Assetbundle中在本地來做,等做的差不多了直接把Assetbundle放在伺服器上,因為兩種讀取的方式完全一樣,這樣以後更新資源會友善很多。然後是讀取,并且加載到遊戲中。

[AppleScript] 純文字檢視 複制代碼 ?

  01 02 03 04 05 06 07 08 09 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

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  obj

=

bundle

.assetBundle.Load

(

"Prefab0"

)

;

Object  obj

1

=

bundle

.assetBundle.Load

(

"Prefab1"

)

;

/

/

加載到遊戲中   

yield

return

Instantiate

(

obj

)

;

yield

return

Instantiate

(

obj

1

)

;

bundle

.assetBundle.Unload

(

false

)

;

}

}

這裡我們詳細的說說 下載下傳類WWW

WWW bundle = new WWW(path);

這樣的做法是通過一個路徑進行下載下傳(無論是伺服器路徑還是本地路徑下載下傳操作都一樣)但是bundle隻能儲存在記憶體中,也就是退出遊戲在進入還得重新下,很顯然在遊戲中我們不能使用這種方式。

[AppleScript] 純文字檢視 複制代碼 ?

  01 02 03 04 05 06 07 08 09 10

<

font

face

=

"Verdana,"

>

private IEnumerator LoadMainCacheGameObject

(

string

path

)

{

WWW

bundle

=

WWW.LoadFromCacheOrDownload

(

path

,

5

)

;

yield

return

bundle

;

/

/

加載到遊戲中

yield

return

Instantiate

(

bundle

.assetBundle.mainAsset

)

;

bundle

.assetBundle.Unload

(

false

)

;

}

<

/

font

>

使用的方法是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重新載入遊戲的時候,它身上腳本的參數也會重新輸出。

Unity3D研究院之Assetbundle的實戰

螢幕快照-2013-06-26-下午3.12.26.png (28.05 KB, 下載下傳次數: 0)

下載下傳附件  儲存到相冊

2014-8-19 16:32 上傳

如果你的Assetbundle中的Prefab上引用的對象,那麼這樣做就會出錯了,你需要設定他們的依賴關系。或者運作時通過腳本動态的載入對象。、http://docs.unity3d.com/Document … etDependencies.html

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

、像這樣重新打包就可以。

3.打包場景

     上面我們說過了打包Prefab,其實我們還可以把整個場景進行打包,因為移動平台不能更新腳本,是以這個功能就會有所限制,我的建議是烘培場景、然後把多個場景可複用的對象移除,場景中隻保留獨一無二的遊戲對象,然後在打包場景,運作遊戲時載入場景後,在動态的将之前移除的對象重新添加進來。可以參考 : Unity3D研究院之将場景導出XML或JSON或二進制并且解析還原場景(四十二)

[AppleScript] 純文字檢視 複制代碼 ?

  01 02 03 04 05 06 07 08 09 10

<

font

face

=

"Verdana,"

>

[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

(

)

;

}

<

/

font

>

        不同平台下需要選擇  BuildTarget.Android 和 BuildTarget.iPhone 。 切記這段代碼是把Level.unity常見檔案打包到MyScene.unity3d檔案中,是以在解包的時候也應當是先解開MyScene.unity3d,然後在去加載Level.unity場景,無需在ProjectSetting中注冊新場景。

[AppleScript] 純文字檢視 複制代碼 ?

  1 2 3 4 5 6

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吧。

Unity3D研究院之Assetbundle的實戰

繼續閱讀