Unity3D使用Assetbundle打包加載(Prefab、場景)


之前有一篇文章中我們相惜討論了Assetbundle的原理,如果對原理還不太了解的朋友可以看這一篇文章:Unity游戲開發使用Assetbundle加載場景的原理 本篇文章我們將說說assetbundle是如何實現的。

 

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方法中進行輸出。

using UnityEngine;  
using System.Collections;  
   
public class Script : MonoBehaviour  
{  
public string name;  
   
void Awake ()  
{  
Debug.Log("my name is "+ name);  
}  
   
}  

 

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

 

 [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中

 [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放在服務器上,因為兩種讀取的方式完全一樣,這樣以后更新資源會方便很多。然后是讀取,並且加載到游戲中。

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只能保存在內存中,也就是退出游戲在進入還得重新下,很顯然在游戲中我們不能使用這種方式。

 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


像這樣重新打包就可以。

 

 

3.打包場景

     上面我們說過了打包Prefab,其實我們還可以把整個場景進行打包,因為移動平台不能更新腳本,所以這個功能就會有所限制,我的建議是烘培場景、然后把多個場景可復用的對象移除,場景中只保留獨一無二的游戲對象,然后在打包場景,運行游戲時載入場景后,在動態的將之前移除的對象重新添加進來。

 

 [MenuItem("Custom Editor/Create Scene")]
    static void CreateSceneALL()
    {
        //清空一下緩存  
        Caching.CleanCache();

        //獲得用戶選擇的路徑的方法,可以打開保存面板(推薦)
        string Path = EditorUtility.SaveFilePanel("保存資源", "SS", "" + "MyScene", "unity3d");

        //另一種獲得用戶選擇的路徑,默認把打包后的文件放在Assets目錄下
        //string Path = Application.dataPath + "/MyScene.unity3d";

        //選擇的要保存的對象 
        string[] levels = { "Assets/Scenes/example.unity" };

        //打包場景  
        BuildPipeline.BuildPlayer(levels, Path, BuildTarget.StandaloneWindows64, BuildOptions.BuildAdditionalStreamedScenes);

        // 刷新,可以直接在Unity工程中看見打包后的文件
        AssetDatabase.Refresh();
    }

 

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

 

private IEnumerator LoadScene()  
{  
  WWW download = WWW.LoadFromCacheOrDownload ("file://"+Application.dataPath + "/MyScene.unity3d", 1);  
  yield return download;  
  var bundle = download.assetBundle;  
  Application.LoadLevel ("example");  
}  

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

Caching.CleanCache();

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM