1、簡介
馬三在公司大部分時間作的都是遊戲業務邏輯和編輯器工具等相關工做,所以對Unity AssetBundle這塊的知識點並非很熟悉,本身也是有打算想了解並熟悉一下AssetBundle,掌握一下Unity資源管理方面的知識,所以有了這篇博客。html
我會在博客中整理出一些本身學習時收集到的一些比較好的AssetBundle學習資料,而且結合如今正在參與開發的商業項目隨時寫下一些本身的拙見。本篇博客權當是馬三本身的筆記和一些雜談,會不斷地隨時隨地的更新一些內容。git
2、收集整理的一些AssetBundle資料
1.Unity AssetBundle5講系列
Unity5-ABSystem(一):AssetBundle原理github
Unity5-ABSystem(二):AssetBundle導出數組
Unity5-ABSystem(三):AssetBundle加載緩存
Unity5-ABSystem(四):AssetBundle依賴安全
Unity5-ABSystem(五):AssetBundle內存網絡
2.慕容小匹夫系列
Unity3D 5.3 新版AssetBundle使用方案及策略數據結構
進擊的AssetBundles和它的工具們多線程
3.何三思譯Unity AssetBundle官方文檔系列
【Unity3D技術文檔翻譯】第1.0篇 AssetBundles框架
【Unity3D技術文檔翻譯】第1.1篇 AssetBundle 工做流
【Unity3D技術文檔翻譯】第1.2篇 爲打包 AssetBundles 準備資產
【Unity3D技術文檔翻譯】第1.3篇 建立 AssetBundles
【Unity3D技術文檔翻譯】第1.4篇 AssetBundle 依賴關係
【Unity3D技術文檔翻譯】第1.5篇 本地使用 AssetBundles
【Unity3D技術文檔翻譯】第1.6篇 使用 AssetBundle Manager
【Unity3D技術文檔翻譯】第1.7篇 AssetBundles 補丁更新
【Unity3D技術文檔翻譯】第1.8篇 AssetBundles 問題及解決方法
【Unity3D技術文檔翻譯】第1.9篇 使用 Unity AssetBundle Browser tool (AssetBundle系列完結)
4.Unity AssetBundle官方文檔
AssetBundlesManual
A guide to AssetBundles and Resources
5.Unity Assetbundles官方說明系列
Unity5.4 Assetbundles官方說明一(AssetBundles打包詳解)
Unity5.4 Assetbundles官方說明二(AssetBundle壓縮與解壓)
Unity5.4 Assetbundles官方說明三(AssetBundle資源包的內部結構)
Unity5.4 Assetbundles官方說明四(AssetBundles的下載和加載)
Unity5.4 Assetbundles官方說明五(從AssetBundles的加載和卸載資源對象)
Unity5.4 Assetbundles官方說明六(保留下載的AssetBundle)
Unity5.4 Assetbundles官方說明七(在AssetBundle中存儲和加載二進制數據)
Unity5.4 Assetbundles官方說明八(數據安全方面的處理)
Unity5.4 Assetbundles官方說明九(資源包中包含腳本文件)
Unity5.4 Assetbundles官方說明十(官方疑難問題解答)
Unity5.4 Assetbundles十一:遇到的坑和整理的打包和加載流程(資源包更新的簡易框架)
6.未規劃分類
Unity AssetBundle 從入門到掌握(適合初學者)
Unity5 如何作資源管理和增量更新
Unity資源處理機制(Assets/WWW/AssetBundle/...)讀取和加載資源方式詳解
Unity3D中實現按資源名稱自動化命名打包AssetBundle
Unity動態加載和內存管理(三合一)
Unity手遊之路<十二>手遊資源熱更新策略探討
Unity5 資源打包控制
Unity AssetBundle加載音頻,沒法播放音效並報錯的坑
Unity打包AssetBundle自動分析資源依賴關係(包括UGUI圖集打包)
Unity最佳實踐-AssetBundle使用模式
Unity 5.x AssetBundle零冗餘解決方案
資源依賴正確性測試
Unity AssetBundle 再回顧
AssetBundle資源打包加載管理
7.AssetBundle分組策略總結
邏輯實體分組
一個UI界面或者全部UI界面一個包(這個界面裏面的貼圖和佈局信息一個包)
一個角色或者全部角色一個包(這個角色裏面的模型和動畫一個包)
全部的場景所共享的部分一個包(包括貼圖和模型)
按照類型分組
全部聲音資源打成一個包,全部shader打成一個包,全部模型打成一個包,全部材質打成一個包
按照使用分組
把在某一時間內使用的全部資源打成一個包。能夠按照關卡分,一個關卡所須要的全部資源包括角色、貼圖、聲音等打成一個包。也能夠按照場景分,一個場景所須要的資源一個包
注意
常常更新的資源放在一個單獨的包裏面,跟不常常更新的包分離
把須要同時加載的資源放在一個包裏面
能夠把其餘包共享的資源放在一個單獨的包裏面
把一些須要同時加載的小資源打包成一個包
若是對於一個同一個資源有兩個版本,能夠考慮經過後綴來區分,例如v一、v二、v3
3、AssetBundle踩坑與經驗集錦
一、先說一個遇到的坑,當大量(幾百個)AssetBundle加載的時候(多是WWW加載的時候,也多是AssetBundle.LoadAsset的時候),Android手機上會閃退。看崩潰log是多線程文件訪問的時候崩潰了。解決方法是減小同時加載的AB數量(這個是純邏輯控制),使用AssetBundle.LoadFromFile接口。
二、打包AssetBundle使用LZ4壓縮(BuildPipeline.BuildAssetBundles,第二個參數傳遞BuildAssetBundleOptions.ChunkBasedCompression),默認是LZMA壓縮的,具備最高的壓縮比。而替換爲LZ4壓縮,壓縮比沒有LZMA高,可是加載速度大幅提升。加載AssetBundle使用AssetBundle.LoadFromFile(Async),在Unity4的時候,只能使用WWW的接口來加載AB,由於CreateFromFile不支持壓縮的AB。而Unity5的LoadFromFile是支持任意壓縮格式的AB的。因此沒有太大必要使用WWW了,並且這個接口像WWW.LoadFromCacheOrDownload接口同樣,加載不壓縮或者LZ4壓縮格式的AB的時候是不會有額外的內存開銷的。具體關於內存、加載速度的細節能夠參考上面第三篇文章裏面的介紹。
三、資源規劃好一個獨立的資源工程。規劃好一系列的文件夾,在導入相應資源的時候自動經過AssetImporter設置好AB的名字。監測資源導入能夠用AssetPostprocessor 。帶動畫的模型須要建立好prefab,而不帶動畫只是用於換裝的模型能夠直接導出,不須要建立prefab,由於這些模型咱們只是取它的mesh數據。若是有打包圖集,須要注意它和AB的匹配關係,舉例來講,若是三張圖片指定了同一個圖集,而又分別指定了不一樣的AB名,則三個AB裏面都包含了此圖集(三張圖片),這樣就會形成嚴重的資源浪費。
四、AssetBundle.LoadFromFile接口在Android平臺下也是能夠直接訪問StreamingAssets文件夾裏面的內容的。5.4版本能夠直接使用Application.streamingAssetsPath。而以前的版本須要使用 Application.dataPath + "!assets/" + filePath; 由於streamingAssetsPath帶了jar://,這個是給WWW用的URL路徑,而LoadFromFile接口須要的是實際路徑(不帶jar://也不帶file://)。注意 !assets/ 這個地方歎號後面沒有/。網上搜索到的各類寫法都有,只有這個是正確的,注意此處細節。
4、Unity AssetBundle爬坑手記
文章轉載自:http://www.cnblogs.com/ybgame/p/3973177.html
這篇文章從AssetBundle的打包,使用,管理以及內存佔用各個方面進行了比較全面的分析,對AssetBundle使用過程當中的一些坑進行填補指引以及噴!
AssetBundle是Unity推薦的資源管理方式,官方列舉了諸如熱更新,壓縮,靈活等等優勢,但AssetBundle的坑是很是深的,不少隱藏細節讓你使用起來須要十分謹慎,一不當心就會掉入深坑,打包沒規劃好,20MB的資源「壓縮」到了30MB,或者大量的包致使打包以及加載時的各類低效,或者莫名其妙地丟失關聯,或者內存爆掉,以及各類加載失敗,在網上研究了大量關於AssetBundle的文章,但每次看完以後,仍是有很多疑問,因此只能經過實踐來解答心中的疑問,爲確保結果的準確性,下面的測試在編輯器下,Windows,IOS下都進行了測試比較。
首先你爲何要選擇AssetBundle,縱使他有千般好處,但通常選擇AssetBundle的緣由就是,要作熱更新,動態更新遊戲資源,或者你Resource下的資源超過了它的極限(2GB仍是4GB?),若是你沒有這樣的需求,那麼建議你不要使用這個壞東西,鬧心~~
當你選擇了AssetBundle以後,以及我開始噴AssetBundle以前,咱們須要對AssetBundle的工做流程作一個簡單的介紹:
AssetBundle能夠分爲打包AssetBundle以及使用AssetBundle
打包須要在UnityEditor下編寫一些簡單的代碼,來取出你要打包的資源,而後調用打包方法進行打包
1 Object obj = AssetDatabase.LoadMainAssetAtPath("Assets/Test.png");
2 BuildPipeline.BuildAssetBundle(obj, null,
3 Application.streamingAssetsPath + "/Test.assetbundle",
4 BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
5 | BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.StandaloneWindows);
在使用的時候,須要用WWW來加載Bundle,而後再用加載出來的Bundle來Load資源。
WWW w = new WWW("file://" + Application.streamingAssetsPath + "/Test.assetbundle");
myTexture = w.assetBundle.Load("Test");
【一,打包】
接下來咱們來看一下打包:
1.資源的蒐集
在打包前咱們能夠經過遍歷目錄的方式來自動化地進行打包,能夠有選擇性地將一些目錄打包成一個Bundle,這塊也能夠用各類配置文件來管理資源,也能夠用目錄規範來管理
我這邊是用一個目錄規範對資源進行大的分類,分爲公共以及遊戲內,遊戲外幾個大模塊,而後用一套簡單命名規範來指引打包,例如用OBO(OneByOne)做爲目錄後綴來指引將目錄下全部資源獨立打包,默認打成一個包,用Base前綴來表示這屬於公共包,同級目錄下的其餘目錄須要依賴於它
使用Directory的GetFiles和GetDirectories能夠很方便地獲取到目錄以及目錄下的文件
Directory.GetFiles("Assets/MyDirs", "*.*", SearchOption.TopDirectoryOnly);
Directory.GetDirectories(Application.dataPath + "/Resources/Game", "*.*", SearchOption.AllDirectories);
2.資源讀取
GetFiles蒐集到的資源路徑能夠被加載,加載以前須要判斷一下後綴是否.meta,若是是則不取出該資源,而後將路徑轉換至Assets開頭的相對路徑,而後加載資源。
string newPath = "Assets" + mypath.Replace(Application.dataPath, "");
newPath = newPath.Replace("\\", "/");
Object obj = AssetDatabase.LoadMainAssetAtPath(newPath);
3.打包函數
咱們調用BuildPipeline.BuildAssetBundle來進行打包:
BuildPipeline.BuildAssetBundle有5個參數,第一個是主資源,第二個是資源數組,這兩個參數必須有一個不爲null,若是主資源存在於資源數組中,是沒有任何關係的,若是設置了主資源,能夠經過Bundle.mainAsset來直接使用它
第三個參數是路徑,通常咱們設置爲 Application.streamingAssetsPath + Bundle的目標路徑和Bundle名稱
第四個參數有四個選項,BuildAssetBundleOptions.CollectDependencies會去查找依賴,BuildAssetBundleOptions.CompleteAssets會強制包含整個資源,BuildAssetBundleOptions.DeterministicAssetBundle會確保生成惟一ID,在打包依賴時會有用到,其餘選項沒什麼意義
第五個參數是平臺,在安卓,IOS,PC下,咱們須要傳入不一樣的平臺標識,以打出不一樣平臺適用的包,
注意,Windows平臺下打出來的包,不能用於IOS
在打對應的包以前應該先選擇對應的平臺再打包
4.打包的決策
在打包的時候,咱們須要對包的大小和數量進行一個平衡,全部資源打成一個包,一個資源打一個包,都是比較極端的作法,他們的問題也很明顯,更多狀況下咱們須要靈活地將他們組合起來
打成一個包的缺點是加載了這個包,咱們不須要的東西也會被加載進來,佔用額外內存,並且不利於熱更新
打成多個包的缺點是,容易形成冗餘,首先影響包的讀取速度,而後包之間的內容可能會有重複,且太多的包不利於資源管理
哪些模塊打成一個包,哪些模塊打成多個包,須要根據實際狀況來,例如遊戲中每一個怪物都須要打成一個包,由於每一個怪物之間是獨立的,例如遊戲的基礎UI,能夠打成一個包,由於他們在各個界面都會出現
PS.想打包進AssetBundle中的二進制文件,文件名的後綴必須爲「.bytes」
解包的第一步是將Bundle加載進來,new一個WWW傳入一個URL便可加載Bundle,咱們能夠傳入一個Bundle的網址,從網絡下載,也能夠傳入本地包的路徑,通常咱們用file://開頭+Bundle路徑,來指定本地的Bundle,用
http://或
https://開頭+Bundle網址來指定網絡Bundle
string.Format("file://{0}/{1}", Application.streamingAssetsPath, bundlePath);
在安卓下路徑不同,若是是安卓平臺的本地Bundle,須要用jar:file://做爲前綴,而且須要設置特殊的路徑才能加載
string.Format("jar:file://{0}!/assets/{1}", Application.dataPath, bundlePath);
傳入指定的URL以後,咱們能夠用WWW來加載Bundle,加載Bundle須要消耗一些時間,因此咱們通常在協同裏面加載Bundle,若是加載失敗,你能夠在www.error中獲得失敗的緣由
IEnumerator LoadBundle(string url)
{
WWW www = = new WWW(url);
yield return www;
if (www.error != null)
{
Debug.LogError("Load Bundle Faile " + url + " Error Is " + www.error);
yield break;
}
//Do something ...
}
除了建立一個WWW以外,還有另外一個方法能夠加載Bundle,
WWW.LoadFromCacheOrDownload(url, version),使用這個函數對內存的佔用會小不少,但每次從新打包都須要將該Bundle對應的版本號更新(第二個參數version),不然可能會使用以前的包,而不是最新的包,LoadFromCacheOrDownload會將Bundle從網絡或程序資源中,解壓到一個磁盤高速緩存,通常能夠理解爲解壓到本地磁盤,若是本地磁盤已經存在該版本的資源,就直接使用解壓後的資源。對於AssetBundle全部對內存佔用的狀況,後面會有一小節專門介紹它
LoadFromCacheOrDownload會記錄全部Bundle的使用狀況,並在適當的時候刪除最近不多使用的資源包,它容許存在兩個版本號不一樣但名字同樣的資源包,這意味着你更新這個資源包以後,若是沒有更新代碼中的版本號,你可能取到的會是舊版本的資源包,從而產生其餘的一些BUG。另外,當你的磁盤空間不足的時候(硬盤爆了),LoadFromCacheOrDownload只是一個普通的new WWW!後面關於內存介紹的小節也會對這個感嘆號進行介紹的
拿到Bundle以後,咱們就須要Load裏面的資源,有Load,LoadAll以及LoadAsyn可供選擇
//將全部對象加載資源
Object[] objs = bundle.LoadAll();
//加載名爲obj的資源
Object obj = bundle.Load("obj");
//異步加載名爲resName,類型爲type的資源
AssetBundleRequest res = bundle.LoadAsync(resName, type);
yield return res;
var obj = res.asset;
咱們常常會把各類遊戲對象作成一個Prefab,那麼Prefab也會是咱們Bundle中常見的一種資源,使用Prefab時須要注意一點,
在Bundle中加載的Prefab是不能直接使用的,它須要被實例化以後,才能使用,而對於這種Prefab,實例化以後,這個Bundle就能夠被釋放了
//須要先實例化
GameObject obj = GameObject.Instantiate(bundle.Load("MyPrefab")) as GameObject;
對於從Bundle中加載出來的Prefab,能夠理解爲咱們直接從資源目錄下拖到腳本上的一個Public變量,是未被實例化的Prefab,只是一個模板
若是你用上面的代碼來加載資源,當你的資源慢慢多起來的時候,你可能會發現一個很坑爹的問題,你要加載的資源加載失敗了,例如你要加載一個GameObject,可是整個加載過程並無報錯,而當你要使用這個GameObject的時候,出錯了,而一樣的代碼,咱們在PC上可能沒有發現這個問題,當咱們打安卓或IOS包時,某個資源加載失敗了。
出現這種神奇的問題,首先是懷疑打包的問題,包太大了?刪掉一些內容,不行!從新打一個?仍是不行!而後發現來來回回,都是這一個GameObject報的錯,難道是這個GameObject裏面部分資源有問題?對這個GameObject各類分析,把它大卸八塊,處理成一個很簡單的GameObject,仍是不行!難道是名字的問題?把這個GameObject的名字改了一下,能夠了!
原本事情到這就該結束了,可是,這也太莫名其妙了吧!並且,最重要的是,哥就喜歡原來的名字!!把這個資源改爲新的名字,怎麼看怎麼變扭,怎麼看都沒有原來的名字好看,因此繼續折騰了起來~
首先單步跟蹤到這個資源的Load,資源被成功Load出來了,可是Load出來的東西有點怪怪的,明顯不是一個GameObject,而是一個莫名其妙的東西,多是Unity生成的一箇中間對象,也許是一個索引對象,反正不是我要的東西,打包的GameObject怎麼會變成這個玩意呢?因而在加載Bundle的地方,把Bundle LoadAll了一下,而後查看這個Bundle裏面的內容
在這裏咱們能夠看到,有一個叫RoomHallView和RoomMainView的GameObject,而且,LoadAll以後的資源比我打包的資源要多不少,看樣子全部關聯到的資源都被自動打包進去了,數組的427是RoomHallView的GameObject,而431纔是RoomMainView的GameObject。能夠看到名字叫作RoomMainView和RoomHallView的對象有好幾個,GameObject,Transform,以及一個只有名字的對象,它的類型是一個ReferenceData。
仔細查看能夠發現,RoomHallView的GameObject是排在數組中全部名爲RoomHallView對象的最前面,而RoomMainView則是ReferenceData排在前面,當咱們Load或者LoadAsyn時,是一次數組的遍歷,當遍歷到名字匹配的對象時,則將對象返回,LoadAsyn會對類型進行匹配,但因爲咱們傳入的是Object,而幾乎全部的對象都是Object,因此返回的結果就是第一個名字匹配的對象
在Load以及LoadAsyn時,除了名字,把要加載對象的類型也傳入,再調試,原來的名字也能夠正常被讀取到了,這個細節很是的坑,由於在官網並無提醒,並且示例的sample也沒有說應該注意這個地方,而且出現問題的概率很小。因此一旦出現,就坑死了
bundle.Load("MyPrefab", typeof(GameObject))
另外,
不要在IOS模擬器上測試AssetBundle,你會收到bad url的錯誤
【三,依賴】
依賴和打包息息相關,之因此把依賴單獨分開來說,是由於這玩意太坑了.......
【1.打包依賴】
在咱們打包的時候,將兩個資源打包成單獨的包,那麼兩個資源所共用的資源,就會被打包成兩份,這就形成了冗餘,因此咱們須要將公共資源抽出來,打成一個Bundle,而後後面兩個資源,依賴這個公共包,那麼還有另一種方法,就是把它們三打成一個包,但這不利於後期維護。
咱們使用BuildPipeline.PushAssetDependencies()和BuildPipeline.PopAssetDependencies()來開啓Bundle之間的依賴關係,當咱們調用PushAssetDependencies以後,會開啓依賴模式,當咱們依次打包 A B C時,若是A包含了B的資源,B就不會再包含這個資源,而是直接依賴A的,若是A和B包含了C的資源,那麼C的這個資源舊不會被打包進去,而是依賴A和B。這時候只要有一樣的資源,就會向前依賴,當咱們但願,B和C依賴A,但B和C之間不互相依賴,就須要嵌套Push Pop了,當咱們調用PopAssetDependencies就會結束依賴
string path = Application.streamingAssetsPath;
BuildPipeline.PushAssetDependencies();
BuildTarget target = BuildTarget.StandaloneWindows;
BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/UI_tck_icon_houtui.png"), null,
path + "/package1.assetbundle",
BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
| BuildAssetBundleOptions.DeterministicAssetBundle, target);
BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/New Material.mat"), null,
path + "/package2.assetbundle",
BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
| BuildAssetBundleOptions.DeterministicAssetBundle, target);
BuildPipeline.PushAssetDependencies();
BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Cube.prefab"), null,
path + "/package3.assetbundle",
BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
| BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.StandaloneWindows);
BuildPipeline.PopAssetDependencies();
BuildPipeline.PushAssetDependencies();
BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Cubes.prefab"), null,
path + "/package4.assetbundle",
BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
| BuildAssetBundleOptions.DeterministicAssetBundle, target);
BuildPipeline.PopAssetDependencies();
BuildPipeline.PopAssetDependencies();
上面的代碼演示瞭如何使用依賴,這個測試使用了一個紋理,一個材質,一個正方體Prefab,還有兩個正方體組成的Prefab,材質使用了紋理,而兩組正方體都使用了這個材質,上面的代碼用Push開啓了依賴,打包紋理,而後打包材質(材質自動依賴了紋理),而後嵌套了一個Push,打包正方體(正方體依賴前面的材質和紋理),而後Pop,接下來再嵌套了一個Push,打包那組正方體(不依賴前面的正方體,依賴材質和紋理)
若是咱們只開啓最外面的Push Pop,而不嵌套Push Pop,那麼兩個正方體組成的Prefab就會依賴單個正方體的Prefab,依賴是一把雙刃劍,它能夠去除冗餘,但有時候咱們又須要那麼一點點冗餘
【2.依賴丟失】
當咱們的Bundle之間有了依賴以後,就不能像前面那樣簡單地直接Load對應的Bundle了,咱們須要
把Bundle所依賴的Bundle先加載進來,這個加載只是WWW或者
LoadFromCacheOrDownload,並不須要對這個Bundle進行Load,若是BundleB依賴BundleA,當咱們要加載BundleB的資源時,假設BundleA沒有被加載進來,或者已經被Unload了,那麼BundleB依賴BundleA的部分就會丟失,例如每一個正方體上都掛着一個腳本,當咱們不嵌套Push Pop時,單個正方體的Bundle沒有被加載或者已經被卸載,咱們加載的那組正方體上的腳本就會丟失,
腳本也是一種資源,當一個腳本已經被打包了,依賴這個包的資源,就不會被再打進去
Cubes和Cube都掛載同一個腳本,TestObje,Cubes依賴Cube,將Cube所在的Bundle Unload,再Load Cubes的Bundle,Cubes的腳本丟失,腳本,紋理,材質等一切資源,都是如此
【3.更新依賴】
在打包的時候咱們須要指定BuildAssetBundleOptions.DeterministicAssetBundle選項,這個選項會爲每一個資源生成一個惟一的ID,當這個資源被從新打包的時候,肯定這個ID不會改變,包的依賴是根據這個ID來的,使用這個選項的好處是,當資源須要更新時,依賴於該資源的其餘資源,不須要從新打包
A -> B -> C
當A依賴B依賴C時,B更新,須要從新打包C,B,而A不須要動,打包C的緣由是,由於B依賴於C,若是不打包C,直接打包B,那麼C的資源就會被重複打包,並且B和C的依賴關係也會斷掉
【四,內存】
在使用WWW加載Bundle時,會開闢一塊內存,這塊內存是Bundle文件解壓以後的內存,這意味着這塊內存很大,經過Bundle.Unload能夠釋放掉這塊內存,Unload true和Unload false 都會釋放掉這塊內存,而這個Bundle也不能再用,若是要再用,須要從新加載Bundle,
須要注意的是,依賴這個Bundle的其餘Bundle,在Load的時候,會報錯
獲得Bundle以後,咱們用Bundle.Load來加載資源,這些資源會從Bundle的內存被複製出來,做爲Asset放到內存中,這意味着,這塊內存,也很大,Asset內存的釋放,與Unity其餘資源的釋放機制同樣,能夠經過Resources.UnloadUnuseAsset來釋放沒有引用的資源,也能夠經過Bundle.Unload(true)來強制釋放Asset,這會致使全部引用到這個資源的對象丟失該資源
上面兩段話能夠得出一個結論,在new WWW(url)的時候,會開闢一塊內存存儲解壓後的Bundle,而在資源被Load出來以後,又會開闢一塊內存來存儲Asset資源,WWW.LoadFromCacheOrDownload(url)的功能和new WWW(url)同樣,但LoadFromCacheOrDownload是將Bundle解壓到磁盤空間而不是內存中,因此LoadFromCacheOrDownload返回的WWW對象,自己並不會佔用過多的內存(只是一些索引信息,每一個資源對應的磁盤路徑,在Load時從磁盤取出),針對手機上內存較小的狀況,
使用
WWW.
LoadFromCacheOrDownload代替new WWW能夠有效地節省內存。但LoadFromCacheOrDownload大法也有不靈驗的時候,當它不靈驗時,L
oadFromCacheOrDownload
返回的WWW對象將佔用和new WWW同樣的內存,因此
無論你的Bundle是如何建立出來的,都須要在不使用的時候,及時地Unload掉。
另外使用LoadFromCacheOrDownload須要注意的問題是——第二個參數,版本號,Bundle從新打包以後,版本號沒有更新,取出的會是舊版本的Bundle,而且一個Bundle緩存中可能會存在多箇舊版本的Bundle,例如1,2,3 三個版本的Bundle
在Bundle Load完以後,不須要再使用該Bundle了,進行Unload,若是有其餘Bundle依賴於該Bundle,則應該等依賴於該Bundle的Bundle不須要再Load以後,Unload這個Bundle,通常出如今大場景切換的時候。
咱們知道在打包Bundle的時候,有一個參數是mainAsset,若是傳入該參數,那麼資源會被視爲主資源打包,在獲得Bundle以後,能夠用AssetBundle.mainAsset直接使用,那麼是否在WWW獲取Bundle的時候,就已經將mainAsset預先Load出來了呢?不是!
在咱們調用
AssetBundle.mainAsset取出mainAsset時,它的get方法會阻塞地去Load mainAsset,而後返回,AssetBundle.mainAsset等同於Load("mainAssetName")
PS.重複Load同一個資源並不會開闢新的內存來存儲這個資源
【五,其餘】
在使用AssetBundle的開發過程當中,咱們常常會對資源進行調整,調整以後須要對資源進行打包才能生效,對開發效率有很大的影響,因此在開發中咱們使用Resource和Bundle兼容的方式
首先將資源管理封裝到一個Manager中,從Bundle中Load資源仍是從Resource裏面Load資源,都由它決定,這樣能夠保證上層邏輯代碼不須要關心當前的資源管理類型
固然,咱們全部要打包的對象,都在Resource目錄下,而且使用嚴格的目錄規範,而後使用腳本對象,來記錄每一個資源所在的Bundle,以及所對應的Resource目錄,在資源發生變化的時候,更新腳本對象,Manager在運行時使用腳本對象的配置信息,這裏的腳本對象咱們是使用代碼自動生成的,固然,你也能夠用配置表,效果也是同樣的
版本管理也能夠交由腳本對象來實現,每次打包的資源,須要將其版本號+1,腳本對象可存儲全部資源的版本號,版本號能夠用於LoadFromCacheOrDownload時傳入,也能夠手動寫入配置表,在我設計的腳本對象中,每一個資源都會有一個所屬Bundle,Resource下相對路徑,版本號等三個屬性
在版本發佈的時候,你須要先打包一次Bundle,而且將Resource目錄改爲其餘的名字,而後再打包,確保Resource目錄下的資源沒有被重複打包,而若是你想打的是Resource版本,則須要將StreamingAssets下的Bundle文件刪除
腳本對象的使用以下:
1.先設計好存儲結構
2.寫一個繼承於ScriptObject的類,用可序列化的容器存儲數據結構(List或數組),Dictionary等容器沒法序列化,public以後在
[Serializable]
public class ResConfigData
{
public string ResName; //資源名字
public string BundleName; //包名字
public string Path; //資源路徑
public int Vesrion; //版本號
}
[System.Serializable]
public class ResConfig : ScriptableObject
{
public List<ResConfigData> ConfigDatas = new List<ResConfigData>();
}
4.在指定的路徑讀取對象,讀取不到則建立對象
ResConfig obj = (ResConfig)AssetDatabase.LoadAssetAtPath(path, typeof(ResConfig));
if (obj == null)
{
obj = ScriptableObject.CreateInstance<ResConfig>();
AssetDatabase.CreateAsset(obj, path);
}
3.寫入數據,直接修改obj的數組,並保存(不保存下次啓動Unity數據會丟失)
EditorUtility.SetDirty(obj);
因爲數組操做不方便,因此咱們能夠將數據轉化爲方便各類增刪操做的Dictionary容器存儲,在保持時將其寫入到持久化的容器中
5、未完待續