使用Assetbundle時可能遇到的坑


原地址:http://www.cnblogs.com/realtimepixels/p/3652128.html

轉自 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,第二次再次利用,則要用已經讀取出來的資源。

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

4.Unity編譯的webplayer工程,免費cache只有50MB
聽起來是不是好驚訝?webplayer下的免費cache居然這么少?意味着,在webplayer下,你的LoadFromCacheOrDownload用處並不是非常大。因為你的資源需要一遍又一遍的下載,cache的作用大大縮小了。而如果你想擴展,需要聯系他們的銷售部,據說價格不菲。不過針對這個問題,現在有人已經利用游覽器的緩存繞過了這個問題,思路比較巧妙,但是涉及到unity官方利益,我在這里就不介紹了。

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

1、Assetbundle打包時需要使用 BuildTarget.iPhone 參數。不同發布平台打包的文件是不通用的。

2、當上傳已經打包好的文件到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 前檢查。


免責聲明!

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



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