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在打包的同時會把模型身上的全部材質、貼圖、組件、腳本所有包含進去。服務器
由此可得相同的模型儘可能打包在一塊兒,他們會公用一套資源文件。不相同的模型儘可能分開打包,相同模型具備不一樣的腳本、組件的話把他們放在不一樣的Prefab中,最後把這些Prefab一塊兒打包在一個Assetbundle中。以下圖所示,如今Project視圖中選擇須要打包的Prefab,而後在導航菜單欄中選擇Create Assetbundles Main表示分別打包、Create AssetBundles All表示將他們打包在一塊兒。異步
這兩個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文件。
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從新載入遊戲的時候,它身上腳本的參數也會從新輸出。
若是你的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吧。
最後是下載地址: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。
此法確實可行,咱們已經在實際項目中轟轟烈烈的使用了。。
轉載請註明: 雨鬆MOMO
2013年06月26日
於 雨鬆MOMO程序研究院 發表