Assetbundle的雜七雜八

使用Assetbundle時可能遇到的坑

web

轉自 http://www.unitymanual.com/blog-3571-132.html緩存

1.Editor版本不能讀取與本身版本不一樣的assetbundle
這個問題描述起來很簡單:好比:老闆原來讓你用4.1打包(BuildAssetBundle)開發,開發完畢後,下半年,unity升級了, 因而老闆要求與時俱進,讓你用4.3開發維護,這時,問題就出現了:4.1下的editor打包的assetbundle在editor下不能被4.3讀 取,會報錯。
那麼解決方法是啥呢?把全部資源從新在4.3下打包。很坑吧?可是,這問題僅僅只是在editor下會出現,webplayer不會出現。但你恐怕不多說調試程序不走editor吧?服務器

2.assetbundle若是從WWW中被讀取過一次,再讀取會報錯
當你把assetbundle資源用www下載下來後,每每都會使用「wwwResource.assetbundle」,可是注意,這個函數調用assetbundle的時候,只能調用一遍。意思是,當你要再次讀取的時候,會報錯。這時候,須要你寫一套對於assetbundle的控制程序,保證第一次讀取資源的時候是使用wwwResource.assetbundle,第二次再次利用,則要用已經讀取出來的資源。app

3.慎用BuildPipeline的PushAssetDependencies()和PopAssetDependencies()功能
在咱們的項目中,打包時,用到了BuildPipeline.PushAssetDependencies()和 BuildPipeline.PopAssetDependencies()的功能,這是unity裏比較好的一項功能,它能夠把unity裏的依賴關係 比較好的保存下來,並正常讀取就能夠還原。好比:一個模型用到了貼圖1,貼圖2,貼圖3,這時,咱們能夠用這種依賴關係的方式,把模型單獨打出來,同時把 貼圖1貼圖2貼圖3也單獨打包出來,這樣的話,若是有別的模型也用到了貼圖123,只須要下載一次,而後在讀進工程的時候,貼圖自動找到模型,並貼上去。 聽起來很美,可是也有問題:嚴格根據依賴關係按順序下載資源。不然,留給你的是一個個白模。咱們被這個問題坑了很久。ide

4.Unity編譯的webplayer工程,免費cache只有50MB
聽起來是否是好驚訝?webplayer下的免費cache竟然這麼少?意味着,在webplayer下,你的LoadFromCacheOrDownload用處並非很是大。由於你的資源須要一遍又一遍的下載,cache的做用大大縮小了。而若是你想擴展,須要聯繫他們的銷售部,聽說價格不菲。不過針對這個問題,如今有人已經利用遊覽器的緩存繞過了這個問題,思路比較巧妙,可是涉及到unity官方利益,我在這裏就不介紹了。函數

在IOS平臺加載Assetbundle的注意事項

post

一、Assetbundle打包時須要使用 BuildTarget.iPhone 參數。不一樣發佈平臺打包的文件是不通用的。ui

二、當上傳已經打包好的文件到FTP服務器時,注意在上傳軟件菜單裏選擇傳輸類型爲二進制格式,而不是默認的 ASCII 格式。

 

AssetBundles (Pro only)

AssetBundles are files which you can export from Unity to contain assets of your choice. These files use a proprietary compressed format and can be loaded on demand by your application. This allows you to stream in content, such as models, textures, audio clips, or even entire scenes separately from the scene in which they will be used. AssetBundles have been designed to simplify downloading content to your application. AssetBundles can contain any kind of asset type recognized by Unity, as determined by the filename extension. If you want to include files with custom binary data, they should have the extension ".bytes". Unity will import these files as TextAssets.

When working with AssetBundles, here's the typical workflow:

During development, the developer prepares AssetBundles and uploads them to a server.

 
                 
     Building and uploading asset bundles
 
  1. Building AssetBundles. Asset bundles are created in the editor from assets in your scene. The Asset Bundle building process is described in more detail in the section for Building AssetBundles
     
  2. Uploading AssetBundles to external storage. This step does not include the Unity Editor or any other Unity channels, but we include it for completeness. You can use an FTP client to upload your Asset Bundles to the server of your choice.

At runtime, on the user's machine, the application will load AssetBundles on demand and operate individual assets within each AssetBundle as needed.

                       Downloading AssetBundles and loading assets from them
 
  1. Downloading AssetBundles at runtime from your application. This is done from script within a Unity scene, and Asset Bundles are loaded from the server on demand. More on that in Downloading Asset Bundles.
     
  2. Loading objects from AssetBundles. Once the AssetBundle is downloaded, you might want to access its individual Assets from the Bundle. More on that in Loading Resources from AssetBundles

Please read this section of the documentation thoroughly to familiarize yourself with the workflow for using AssetBundles, discover the different features they provide and learn best practices that can save you time and effort during development.

See also:

Building AssetBundles

There are three class methods you can use to build AssetBundles:

An example of how to build an AssetBundle

Building asset bundles is done through editor scripting. There is basic example of this in the scripting documentation for BuildPipeline.BuildAssetBundle.

 

  BuildAssetBundle

 

For the sake of this example, copy and paste the script from the link above into a new C# script called ExportAssetBundles. This script should be placed in a folder named Editor, so that it works inside the Unity Editor.

Now in the Assets menu, you should see two new menu options.

 
  1. Build AssetBundle From Selection - Track dependencies. This will build the current object into an asset bundle and include all of its dependencies. For example if you have a prefab that consists of several hierarchical layers then it will recursively add all the child objects and components to the asset bundle. 
  2. Build AssetBundle From Selection - No dependency tracking. This is the opposite of the previous method and will only include the single asset you have selected.

For this example, you should create a new prefab. First create a new Cube by going to GameObject -> Create Other -> Cube, which will create a new cube in the Hierarchy View. Then drag the Cube from the Hierarchy View into the Project View, which will create a prefab of that object.

You should then right click the Cube prefab in the project window and select Build AssetBundle From Selection - Track dependencies. At this point you will be presented with a window to save the "bundled" asset. If you created a new folder called "AssetBundles" and saved the cube as Cube.unity3d, your project window will now look something like this.

 

At this point you can move the AssetBundle Cube.unity3d elsewhere on your local storage, or upload it to a server of your choice.

 

An example of how to change the properties of the assets when building an Asset Bundle

You can use AssetDatabase.ImportAsset to force reimporting the asset right before calling BuildPipeline.BuildAssetBundle, and then use AssetPostprocessor.OnPreprocessTexture to set the required properties. The following example will show you how to set different texture compressions when building the Asset Bundle.

 

C#

// Builds an asset bundle from the selected objects in the project view,
// and changes the texture format using an AssetPostprocessor.

using UnityEngine;
using UnityEditor;

public class ExportAssetBundles {

	// Store current texture format for the TextureProcessor.
	public static TextureImporterFormat textureFormat;

	[MenuItem("Assets/Build AssetBundle From Selection - PVRTC_RGB2")]
	static void ExportResourceRGB2 () {
		textureFormat = TextureImporterFormat.PVRTC_RGB2;
		ExportResource();		
	}	

	[MenuItem("Assets/Build AssetBundle From Selection - PVRTC_RGB4")]
	static void ExportResourceRGB4 () {
		textureFormat = TextureImporterFormat.PVRTC_RGB4;
		ExportResource();
	}

	static void ExportResource () {
		// Bring up save panel.
		string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");

		if (path.Length != 0) {
			// Build the resource file from the active selection.
			Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);

			foreach (object asset in selection) {
				string assetPath = AssetDatabase.GetAssetPath((UnityEngine.Object) asset);
				if (asset is Texture2D) {
					// Force reimport thru TextureProcessor.
					AssetDatabase.ImportAsset(assetPath);
				}
			}

			BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets);
			Selection.objects = selection;
		}
	}
}
 

C#

// Changes the texture format when building the Asset Bundle.

using UnityEngine;
using UnityEditor;

public class TextureProcessor : AssetPostprocessor
{   
	void OnPreprocessTexture() {
		TextureImporter importer = assetImporter as TextureImporter;
		importer.textureFormat = ExportAssetBundles.textureFormat;
	}
}

You can also control how the asset is imported using the AssetDatabase.ImportAssetOptions.

In a test environment, you sometimes need to test a change that require AssetBundles to be rebuilt. In these cases, it is advisable to use the option BuildAssetBundleOptions.UncompressedAssetBundle when you build the AssetBundles. This makes it faster to build and load the AssetBundles but they will also be bigger and therefore take longer to download.

 

Building AssetBundles in a production enviroment

When first using AssetBundles it may seem enough to manually build them as seen in the previous example. But as a project grows in size and the number of assets increases doing this process by hand is not efficient. A better approach is to write a function that builds all of the AssetBundles for a project. You can, for example, use a text file that maps Asset files to AssetBundle files.

back to AssetBundles Intro

Streaming Assets

Application.dataPath

Contains the path to the game data folder (Read Only).

The value depends on which platform you are running on:
Unity Editor: < path to project folder>/Assets
Mac player: < path to player app bundle>/Contents
iPhone player: < path to player app bundle>/< AppName.app>/Data
Win player: < path to executablename_Data folder>
Web player: The absolute url to the player data file folder (without the actual data file name)
Flash: The absolute url to the player data file folder (without the actual data file name)
Note that the string returned on a PC will use a forward slash as a folder separator.
 

Application.streamingAssetsPath

Contains the path to the StreamingAssets folder (Read Only).

If you have a "StreamingAssets" folder in the Assets folder of your project, it will be copied to your player builds and be present in the path given by Application.streamingAssetsPath.

Any files placed in a folder called StreamingAssets in a Unity project will be copied verbatim to a particular folder on the target machine.

It's always best to use Application.streamingAssetsPath to get the location of the StreamingAssets folder, it will always point to the correct location on the platform where the application is running.

Most assets in Unity are combined into the project when it is built. However, it is sometimes useful to place files into the normal filesystem on the target machine to make them accessible via a pathname. An example of this is the deployment of a movie file on iOS devices; the original movie file must be available from a location in the filesystem to be played by the PlayMovie function.

Note that on some platforms it is not possible to directly access the StreamingAssets folder because there is no file system access in the web platforms, and because it is compressed into the .apk file on Android. On those platforms, a url will be returned, which can be used using the WWW class.
	// print the path to the streaming assets folder
	var filePath = System.IO.Path.Combine(Application.streamingAssetsPath, "MyFile");
	var result = "";
	if (filePath.Contains("://"))
	{
		var www = new WWW (filePath);
		yield www;
		result = www.text;
	}
	else
		result = System.IO.File.ReadAllText(filePath);

 

 

You can retrieve the folder using theApplication.streamingAssetsPath property. For reference, the location of this folder varies per platform:

On a desktop computer (Mac OS or Windows) the location of the files can be obtained with the following code:

  
path = Application.dataPath + "/StreamingAssets";

On iOS, you should use:

  
path = Application.dataPath + "/Raw";

...while on Android, you should use:

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

Note that on Android, the files are contained within a compressed .jar file (which is essentially the same format as standard zip-compressed files). This means that if you do not use Unity's WWW class to retrieve the file then you will need to use additional software to see inside the .jar archive and obtain the file.

 

 

Unity 3D : 製做與載入 AssetBundle

 

一般我們在遊戲程式執行過程,並不但願一次將所有的資源都載入,而比較但願實際上有使用到的才載入,以避免佔用多餘的記憶體,因此我們可能會儘量規劃好不一樣功能的場景,在須要時才載入場景並釋放掉前個場景中不須要的資源,或是將資源放在 Resource 資料夾中,在真正須要時才利用 Resources.Load() 把資源載入;這些都是不錯的管理方法,可是當我們遊戲中的資源相當多時,輸出的程式還是會相當龐大,並且若是是時常會更新內容資源的遊戲,也會因為龐大的資源而形成編譯輸出時要花相當多的時間;特別是手機或網頁遊戲,幾乎輸出最後的結果只是一個檔案而已,這個檔案若是很龐大,將可能形成下載或啟動遊戲的不方便;這時候我們可能就要考慮製做 AssetBundle。


製做與遊戲主程式分離的資源包能夠獲得某程度上的好處,我們能夠讓遊戲主程式只管理遊戲邏輯的部份,當有須要某些資源則從外部的 AssetBundle 載入資源,這樣一來,若是有任何遊戲更新只是添加或更換資源,並沒有變更到遊戲程式邏輯或內容部署,我們只須要從新打包資源的部份就能完成更新,而再也不須要整個遊戲從新編譯輸出。

例如我們有已上架的 iPhone 遊戲,因為特定節日想把遊戲內的圖片換成另外一種氣氛,一般就必須要從新輸出,然後送審,等節日過後又想把圖片換回本來的,要再次輸出遊戲,再送審,一方面送審費時費工,另外一方面是從送審到架上遊戲更新完成的時間,我們並不能準確掌控;若是事先將資源分離打包成 AssetBundle 儲存在我們本身管理的伺服器中,在這種須要換圖的時刻,只須要打包自行在伺服器中將 AssetBundle 更新便可,能夠省掉不斷送審的麻煩,時間上也更能掌控,另外就是遊戲主程式中沒有龐大的資源資料,主程式也就會小一點,那麼玩家要下載我們的遊戲也會更快速些。

還有就是,我們輸出為網頁遊戲時必定會發現到,輸出結果只是一個 html 檔及一個 unity3d 檔,應該有寫過網頁的人都知道,網頁的運做是瀏覽器將網頁中的文件、圖片、音樂、影像... 等等的資料下載到客戶端暫存之後再執行呈現出結果,那麼若是我們遊戲內容的龐大資源都與主程式編在一塊兒的話,那麼輸出後的 unity3d 檔應該也會不小,此時我們可能要思考一下,我們但願玩家花多久時間完成啟動頁面再進行遊戲呢?也許 Unity 的 Web Player 有辦法解決這個問題使玩家不會花太多時間啟動頁面(這部份我就比較不確定了^_^),可是正因為網頁的運做方式是如此,因此當我們在伺服器端更新時,玩家只要沒有從新載入頁面,玩到的遊戲內容始終是舊版的,此時又要考慮到,若是沒有更新遊戲邏輯或內容規則的情況下,是否有必要強制玩家中斷遊戲從新載入頁面;若是將資源都打包成 AssetBundle 分離出來,那麼 unity3d 檔這個主程式的部份將會變得更小,而當有任何資源更新時只須要在伺服器端將 AssetBundle 的檔案換掉,玩家不須要從新載入頁面一樣能體驗到更新後的內容。

以上寫了一大堆,主要就是要說明 AssetBundle 是個很重要又好用的東西,畢竟在沒變更程式或遊戲部署的情況下,沒必要去從新 Build 遊戲主程式,如下就來說明如何製做 AssetBundle ...

首先要使用【 自訂 Unity 工具列選單處理專案內容】的方法自定義選單命令,使我們能夠操做何時開始製做 AssetBundle 及如何作,接下來我們須要能夠指定哪些資源將打包成 AssetBundle,因此須要使用到 Editor class 的 Selection.GetFiltered(),還有就是要過濾哪些型別的資源纔是我們要的,選擇錯誤則略過,不要處理,最後再使用 BuildPipeline.BuildAssetBundle() 創建 AssetBundle 便可,如下範例會在 Unity 專案資料夾(不是 Asset 資料夾)中創建 _AssetBunldes 資料夾用來存放打包好的 AssetBundle,打包的目標為 GameObject、Texture2D、Material 三種型別的資源,若是儲存檔案路徑檔名相同則會先刪除舊檔:

[MenuItem("Custom Editor/Create AssetBunldes")]
static void ExecCreateAssetBunldes(){

// AssetBundle 的資料夾名稱及副檔名
string targetDir = "_AssetBunldes";
string extensionName = ".assetBunldes";

//取得在 Project 視窗中選擇的資源(包含資料夾的子目錄中的資源)
Object[] SelectedAsset = Selection.GetFiltered(typeof (Object), SelectionMode.DeepAssets);

//創建存放 AssetBundle 的資料夾
if(!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);

foreach(Object obj in SelectedAsset){

//資源檔案路徑
string sourcePath = AssetDatabase.GetAssetPath(obj);

// AssetBundle 儲存檔案路徑
string targetPath = targetDir + Path.DirectorySeparatorChar + obj.name + extensionName;

if(File.Exists(targetPath)) File.Delete(targetPath);

if(!(obj is GameObject) && !(obj is Texture2D) && !(obj is Material)) continue;

//創建 AssetBundle
if(BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)){

Debug.Log(obj.name + " 創建完成");

}else{

Debug.Log(obj.name + " 創建失敗");
}
}
}

以上是直接將選擇的資源打包成 AssetBundle,另外,你也能夠在打包前依需求將型別為 GameObject 的資源 Instantiate 到場景中添加須要的 Component 並回到 Project 中創建 Prefab,最後再以這個 Prefab 打包成 AssetBundle,一切就看個人如何運用了。

製做好的 AssetBundle 最終目的還是要在遊戲中載入,如下用簡短的範例在遊戲畫面上顯示一個按鈕,當按下按鈕之後載入型別為 GameObject 的 AssetBundle 並利用 Instantiate() 使其實例化而在場景中產生 GameObject:

void OnGUI(){

if(GUI.Button(new Rect(5,35,100,25) , "Load GameObject")){

StartCoroutine(LoadGameObject());
}
}

private IEnumerator LoadGameObject(){

// AssetBundle 檔案路徑
string path = string.Format("file://{0}/../_AssetBunldes/{1}.assetBunldes" , Application.dataPath , "TestGameObject");

//  載入 AssetBundle
WWW bundle = new WWW(path);

//等待載入完成
yield return bundle;

//實例化 GameObject 並等待實做完成
yield return Instantiate(bundle.assetBundle.mainAsset);

//卸載 AssetBundle
bundle.assetBundle.Unload(false);
}

範例中的檔案路徑(path)字串是以此程式碼在 Unity 編輯器中執行為例,實做時應該以實際執行平臺及個人喜愛指定 AssetBundle 存放路徑,詳情可參考官方文件 Application.dataPath 說明;因為載入的資源必須要等待載入完成以確保遊戲運做正常,在 C# 使用 yield 除了回傳型別為 IEnumerator 以外,調用時也必須使用 StartCoroutine() 調用,若是使用 Javascript 則可直接呼叫該 function;若是 AssetBundle 已載入過,下次再請求載入將會出現 【The asset bundle '檔名' can't be loaded because another asset bundle with the same files are already loaded】的錯誤訊息,即便使用區域變數(範例中的 bundle)仍然會獲得這個錯誤訊息,因此必須在每次載入使用完後將 AssetBundle 卸載;另外,要特別注意的是載入 AssetBundle 到卸載期間可能消耗些微時間,若是連續調用載入同一個 AssetBundle,形成在卸載前重複載入也可能出現前面的錯誤訊息,因此,最好是能定義個陣列或其它變數來記錄載入中的 AssetBundle 有哪些,以此變數內容在載入 AssetBundle 前檢查。
相關文章
相關標籤/搜索