Unity AssetBundle Variants 機制研究筆記
最近使用 AssetBundle Variants 機制來打包遊戲專案,紀錄打包遇到的問題以及解決筆記。關於 AssetBnudles 機制可以參考系列文章。
用過 AssetBundle Variants 機制將遊戲打包成數多個語言版本,根據語言設定載入對應語言的 AssetBundles,成功完成多語言遊戲功能後,就放棄自行開發語言切換功能,因為這機制實在太方便了,又可減少玩家所需下載資料量。
關於 AssetBundle Variants
在 Unity 中 AssetBundles 機制能將遊戲內容打包成數多包的資源包,製作更新遊戲內容的機制。
而 AssetBnudle Variants 機制提供另一種資源打包方式,將資源包根據需求打包成不同版本,在遊戲進行中根據需求載入不同版本的資源包,進而更換不同版本的遊戲。載入不同版本的 AssetBundles,其遊戲內部資源參考將不會遺失 (Resource reference)。
例如可以根據語言種類打包 UI 貼圖,使用 AssetBundle Variants 機制將資源打包成中文、英文與日文版本等三個 AssetBundles,遊戲過程中,根據玩家語言設定載入對應語言包的 AssetBundles,讓遊戲 UI 自動使用不同語言版本的貼圖。
亦可以根據現實節日,跟遊戲貼圖以及資源打包聖誕節包、新年節包以及端午節包,根據現實時間修改設定,讓遊戲端載入不同節日的資源包,使得玩家也能在遊戲中享受過節日的氣氛。
簡易的測試專案
測試專案打包架構,Player 表示主程式資源,包含場景 Main;level 為 assetbundle,包含場景 Level,並且使用到 ~/Spring/Main.png;data.spring & data.fall 使用 variant 機制打包的兩個 assetbundles,包含相同檔名的 Main.png,但圖片內容不同
建立一個 AssetBundleVairants 的測試專案,其架構請參考上圖,進入場景為 Main (Entry scene),其場景內的程式會將 AssetBundle level 載入,並根據設定載入 data.spring 或是 data.fall,場景 Level 會根據載入資源不同,而顯示不同的圖片。
完整測試專案程式碼請參考 Github。
AssetBundle Variants 運作原理
專案中資源的參考,場景 Level 使用 [GUID: c9ad] 來連結使用專案中的一張貼圖 ~/Spring/Main.png,Unity 會為每一個專案資源建立唯一的 GUID 來辨識以及連結資源
使用 AssetBundle Variants 打包後的資源參考,把包成兩個 AssetBundles,data.spring & data.fall,Unity 會根據資源檔名來建立一組新的 ID,場景 Level 改用該組新 ID 來連結資源。若載入 data.spring,則使用 ~/Spring/Main.png;反之載入 data.fall,則使用 ~/Fall/Main.png
關於 AssetBundle 檔名
自行使用 Unity 底層 API 打包 AssetBundle 時,須設定此結構 AssetBundleBuild:
strcut AssetBundleBuild
{
public string assetBundleName;
public string assetBundleVariant
public string[] assetNames;
}
若 assetBundleVariant 設定為空字串或是 null,其打包出來的 AssetBundle 檔名為 assetBundleName,該名稱可設定 name.extension 的形式,例如設定 assetBundleName 為 scenes.assetbundle。
若 assetBundleVariant 有設定 (啟用 variant 方式),其打包出來的 AssetBundle 檔名為 assetBundleName.assetBundleVariant,注意 assetBundleVariant 名稱有限制不得設定 Dot (.) 符號
如果要客製化打包出的 AssetBundles 擁有副檔名,最好的方式是打包完後,再使用 System.IO.File.MoveFile
修改其檔案名稱。
設定 Variants 打包機制遇到的錯誤整理
Error: Variant folder path cannot be empty
AssetBundleBuild 有設定 AssetBundleVariant,但其欲打包 Assets 卻沒有在 Importer 中設定 AssetBundleName 所造成出現錯誤,參考以下解決方案:
- 人工解法:在 Project View 選擇欲打包的 Assets,在 Inspector 編輯器下面設定 AssetBundleName
- 程式解法:讀取 Assets 的 AssetImporter 並且設定其值
var importer = AssetImporter.GetAtPath(assetpath); importer.assetBundleName = "example"; importer.assetBundleVariant = "example"; importer.SaveAndReimport();
注意:Asset Importer 設定的 AssetBundleName & AssetBundleVariant 不一定要與 AssetBundleBuild 中的 AssetBundleName & AssetBundleVariant 一樣
Error: AssetBundle names “data.spring” and “data” can’t exist in the same build as “data.spring” has the variant.
若 AssetBundleName ‘data’ 已經確定有使用到 Variant 機制,便不能再打包一個相同名字的 AssetBundle 卻沒有設定 Vairant。例如以下建置方式便會發生此錯誤:
var assetBundleBuilds = new AssetBundleBuild[]
{
new AssetBundleBuild
{
assetBundleName = "data",
assetBundleVariant = "spring",
assetNames = new string[] { "Assets/Data/Spring/Main.png" }
},
new AssetBundleBuild
{
assetBundleName = "data",
assetBundleVariant = "", // <- Cause error
assetNames = new string[] { "Assets/Data/Fall/Main.png" }
}
};
BuildPipeline.BuildAssetBundles(outputPath, assetBundleBuilds, BuildAssetBundleOptions.None, BuildTarget.Android);
Error: Building AssetBundle failed because hash collision was detected in the deterministic id generation.
AssetBundle Variants 機制中,Unity 採用 Assets 檔名來產生新的 id,該 id 必定唯一,其用來內部資源的參考 (Reference)。若一包使用 Variant 機制的 AssetBundle,存在相同檔名的 Assets 時便會發生此錯誤。解決方法也很容易,需要修改檔名即可。
以下會發生此建置錯誤的範例:
var assetBundleBuilds = new AssetBundleBuild[]
{
new AssetBundleBuild
{
assetBundleName = "data",
assetBundleVariant = "spring",
assetNames = new string[] {
"Assets/Data/Spring/Main.png",
"Assets/Data/Fall/Main.png", // <- Cause error
}
},
};
BuildPipeline.BuildAssetBundles(outputPath, assetBundleBuilds, BuildAssetBundleOptions.None, BuildTarget.Android);
Error: Inconsistent asset when sorting preload assets (Same path & file ID)
考慮以下 AssetBundle 打包:
var assetBundleBuilds = new AssetBundleBuild[]
{
new AssetBundleBuild
{
assetBundleName = "data",
assetBundleVariant = "spring",
assetNames = new string[] { "Assets/Data/Spring/Main.png" }
},
new AssetBundleBuild
{
assetBundleName = "data",
assetBundleVariant = "fall",
assetNames = new string[] { "Assets/Data/Fall/Main.png" }
}
};
在專案中同時引用到 Assets/Data/Spring/Main.png 以及 Assets/Data/Fall/Main.png 的貼圖資源所造成的錯誤,修改專案來修正這個問題。
小結
打包 AssetBundles with variant 應注意以下事項:
- 同一包內的資源檔名不能重複,因此將欲打包在同一包的資源,都放在同一層的資料夾最為穩妥,讓作業系統為我們檢查檔案必須唯一
- Prefab 不是一個正常的 Asset 資源,沒辦法使用 AssetBundleVariant 機制自動切換資源參考 * 得使用 AssetBundle.LoadAsset 來載入 Prefab
- 應該根據 Assets 所設定的 AssetBundleName & Variant 來建立 AssetBundleBuilds,讓編輯者根據需求來設定打包,而不要像測試範例寫死
- 讓編輯者承擔設定錯誤的風險 (?)
- 之後想到再補上……
系列文章
- Unity AssetBundle 快取機制與載入範例
- 在 Unity 使用 AssetBundles 實作簡易的遊戲資源打包以及更新機制
- 在 Unity 使用 AssetBundles 實作進階的遊戲資源打包以及更新機制
- Unity AssetBundle Variants 機制研究筆記
- Unity AssetBundle 資料列表載入以及打包架構思考,使用 Pokémon 作為範例
沒有留言: