【Unity3D】基于AssetBundle实现资源热更新
1 前言
? ? ? ? Unity3D 本地資源一般放在 Resources 目錄下,但是 Resouces 文件夾的大小不能超過 2G,使用 AssetBundle 管理資源可以解決 Resources 文件夾受限問題。
? ? ? ? 本文代碼資源見→基于AssetBundle實(shí)現(xiàn)資源熱更新。
????????AssetBundle 主要用于管理資源,配合 AssetDatabase 和 AssetImporter 可以實(shí)現(xiàn)資源重命名,配合 BuildPipeline 可以實(shí)現(xiàn)資源壓縮,配合 WWW 或 UnityWebRequest 可以實(shí)現(xiàn)加載服務(wù)器資源。下面簡單介紹下相關(guān)接口:
? ? ? ? 1)AssetBundle 獲取資源名,加載、卸載資源
? ? ? ? 靜態(tài)方法:?
// 從文件中加載AssetBundle public static AssetBundle LoadFromFile(string path) // 從二進(jìn)制數(shù)組中加載AssetBundle public static AssetBundle LoadFromMemory(byte[] binary) // 從流中加載AssetBundle public static AssetBundle LoadFromStream(Stream stream) // 卸載所有AssetBundle public static void UnloadAllAssetBundles(bool unloadAllObjects) // 銷毀對(duì)象 public static void Destroy(Object obj)? ? ? ? 實(shí)例方法:?
// 獲取所有資源名 public string[] GetAllAssetNames() // 判斷是否包含資源 public bool Contains(string name) // 加載資源 public Object[] LoadAllAssets() public T[] LoadAllAssets<T>() where T : Object public Object[] LoadAllAssets(Type type) public Object LoadAsset(string name) public T LoadAsset<T>(string name) where T : Object public Object LoadAsset(string name, Type type) // 卸載資源, unloadAllLoadedObjects為false時(shí)不卸載已從Bundle中加載出的資源 public void Unload(bool unloadAllLoadedObjects)? ? ? ? 說明:入?yún)?name 不區(qū)分大小寫,建議使用小寫,如果使用大寫會(huì)自動(dòng)轉(zhuǎn)換為小寫。
? ? ? ? 2)AssetBundleManifest 獲取資源依賴
// 獲取所有AssetBundles public string[] GetAllAssetBundles() // 獲取指定assetBundleName的直接依賴 public string[] GetDirectDependencies(string assetBundleName) // 獲取指定assetBundleName的所有依賴 public string[] GetAllDependencies(string assetBundleName)????????說明:入?yún)?assetBundleName 不區(qū)分大小寫,建議使用小寫,如果使用大寫會(huì)自動(dòng)轉(zhuǎn)換為小寫。?
? ? ? ? 3)AssetDatabase 獲取所有資源名、刪除資源
// 獲取所有AssetBundle資源名 public static string[] GetAllAssetBundleNames() // 根據(jù)assetBundleName刪除AssetBundle public static bool RemoveAssetBundleName(string assetBundleName, bool forceRemove) // 刷新Project視圖目錄, 相當(dāng)于右鍵手動(dòng)刷新 public static void Refresh()? ? ? ? 4)AssetImporter 設(shè)置資源名
// 獲取AssetImporter, 資源文件路徑 public static AssetImporter GetAtPath(string path) // 獲取/設(shè)置資源文件名 public string assetBundleName { get; set; }? ? ? ? 5)BuildPipeline 壓縮資源
// 壓縮所有標(biāo)記為AssetBundle的資源 public static AssetBundleManifest BuildAssetBundles(string outputPath, // 壓縮文件輸出路徑BuildAssetBundleOptions assetBundleOptions, // 壓縮算法BuildTarget targetPlatform // 平臺(tái) ) // BuildAssetBundleOptions.None: LZMA壓縮算法, 壓縮比大, 加載慢, 使用前需要整體解壓 // BuildAssetBundleOptions.ChunkBasedCompression: LZ4壓縮算法, 壓縮比中等, 加載快可以加載指定資源而不用解壓全部 // BuildAssetBundleOptions.UncompressedAssetBundle: 不壓縮, 加載快? ? ? ? 6)WWW 獲取網(wǎng)絡(luò)資源
// 獲取WWW public static WWW LoadFromCacheOrDownload(string url, int version) // 獲取AssetBundle public AssetBundle assetBundle { get; }? ? ? ? 說明:WWW 被 Unity3D?官方標(biāo)記為過時(shí)了,建議使用 UnityWebRequest。
? ? ? ? ?7)UnityWebRequest 獲取網(wǎng)絡(luò)資源
UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri) yield return webRequest.SendWebRequest() AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest)2 資源命名
? ? ? ? Asset 資源主要有腳本、圖片、網(wǎng)格、模型、預(yù)設(shè)體等,在 Assets 窗口選中資源,在 Inspector 窗口選擇 AssetBundle 下拉列表,選擇 New 給資源添加 AssetBundle 名,如下:
? ? ? ? 說明:AssetBundle 名不區(qū)分大小寫,如果輸入大寫會(huì)自動(dòng)轉(zhuǎn)換為小寫。只有添加了?AssetBundle 名的資源才能通過 BuildPipeline.BuildAssetBundles()?打包壓縮。
3 資源壓縮
? ? ? ? 1)創(chuàng)建目錄及原資源?
? ? ? ? 在 Assets?目錄下創(chuàng)建 AssetBundles 目錄(存放資源)和 Editor 目錄(存放資源壓縮腳本),在 AssetBundles 目錄下創(chuàng)建 Compress 目錄(存放壓縮文件)和 Raw 目錄(存放原資源文件),再在 Raw 目錄下創(chuàng)建 Textures 目錄(存放了一張圖片 Picture)、Materials 目錄(存放了一個(gè)材質(zhì) Material,并且依賴 Picture)、Prefabs 目錄(存放了一個(gè)預(yù)設(shè)體 Quad,并且依賴 Material),目錄結(jié)構(gòu)如下:
? ? ? ? 2)自動(dòng)壓縮腳本?
????????AssetCompressor.cs
using System.IO; using UnityEditor; using UnityEngine;public class AssetCompressor : Editor {// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Rawprivate static string rawPath = Application.dataPath + "/AssetBundles/Raw";// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compressprivate static string compressPath = Application.dataPath + "/AssetBundles/Compress";[MenuItem("AssetBundle/CompressAssets")]public static void CompressAssets() { // 打包rawPath目錄的資源, 生成壓縮資源到compressPath目錄ClearAllFilesBundleName();SetAssetBundlesName(rawPath);BuildAssetBundles();ClearAllFilesBundleName();AssetDatabase.Refresh(); // 刷新Project視圖目錄, 相當(dāng)于右鍵手動(dòng)刷新}private static void BuildAssetBundles() { // 壓縮資源BuildPipeline.BuildAssetBundles(compressPath, // 壓縮包輸出包路徑BuildAssetBundleOptions.ChunkBasedCompression, // 壓縮算法BuildTarget.StandaloneWindows64 // Windows平臺(tái));}private static void ClearAllFilesBundleName() { // 刪除所有AssetBundle名string[] names = AssetDatabase.GetAllAssetBundleNames();foreach (string name in names) {AssetDatabase.RemoveAssetBundleName(name, true);}}private static void SetAssetBundlesName(string rootPath) { // 設(shè)置資源的Bundle名DirectoryInfo rootInfo = new DirectoryInfo(rootPath);FileSystemInfo[] fileInfos = rootInfo.GetFileSystemInfos();foreach (FileSystemInfo fileInfo in fileInfos) {if (fileInfo is DirectoryInfo) {SetAssetBundlesName(fileInfo.FullName); // 遞歸遍歷子文件夾下所有文件} else if (!fileInfo.Name.EndsWith(".meta")) {SetAssetBundleName(fileInfo.FullName);}}}private static void SetAssetBundleName(string filePath) { // 設(shè)置資源的Bundle名// 導(dǎo)入的相對(duì)路徑(G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/prefabs/quad.prefab)string impoterPath = "Assets/" + filePath.Substring(Application.dataPath.Length + 1);AssetImporter assetImporter = AssetImporter.GetAtPath(impoterPath);if (assetImporter != null) {filePath = filePath.Substring(rawPath.Length + 1); // 去源文件前綴(可選, 建議使用)// filePath = filePath.Substring(filePath.LastIndexOf("\\") + 1); // 去所有前綴(可選, 不建議使用)// 去后綴(可選, 不去后綴刷新目錄后會(huì)報(bào)錯(cuò), 但不影響資源壓縮和后續(xù)資源加載)// filePath = filePath.Remove(filePath.LastIndexOf("."));assetImporter.assetBundleName = filePath;}} }? ? ? ? ?說明:AssetCompressor.cs 文件需要放在 Editor 目錄下,編譯成功后,在菜單欄可以看到 AssetBundle 菜單,如下:
? ? ? ? ?點(diǎn)擊?CompressAssets 選項(xiàng),會(huì)將 Assets/AssetBundles/Raw 目錄下的資源打包壓縮至?Assets/AssetBundles/Compress 目錄,如下:
? ? ? ? ?注意:運(yùn)行上述代碼后,會(huì)報(bào)以下錯(cuò)誤,這是因?yàn)槲募呀?jīng)壓縮了,但還是以 “.prefab”、“.jpg”、“.mat” 為后綴,被 Unity3D 識(shí)別為損壞文件。該錯(cuò)誤不影響壓縮文件生成,也不影響后續(xù)資源加載,可以忽略。如果不想出現(xiàn)以下報(bào)錯(cuò),可以將去后綴的注釋代碼打開。
? ? ? ? ?3)壓縮文件
? ? ? ? ?打開 Compress.manifest 文件如下:
ManifestFileVersion: 0 CRC: 3680739267 AssetBundleManifest:AssetBundleInfos:Info_0:Name: materials/material.matDependencies:Dependency_0: textures/picture.jpgInfo_1:Name: prefabs/quad.prefabDependencies:Dependency_0: materials/material.matInfo_2:Name: textures/picture.jpgDependencies: {}? ? ? ? ?說明:后續(xù)要加載資源時(shí),如果不清楚 AssetBundle 名,可以在 Compress.manifest 文件中查看相應(yīng) Name 值。可以看到,這里的 Name 值也全都自動(dòng)轉(zhuǎn)換為小寫了,在加載資源時(shí),如果傳入大寫的也能正常獲取到相應(yīng)資源。
4 加載本地資源
? ? ? ? 1)加載簡單資源
// targetPath="ptextures/picture.jpg" public static T LoadAsset<T>(string targetPath) { // 加載資源// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/ptextures/picture.jpgAssetBundle targetBundle = AssetBundle.LoadFromFile(compressPath + "/" + targetPath);string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1); // picture.jpgobject obj = targetBundle.LoadAsset(fileName);if (obj != null) {return (T) obj;}return default(T); }? ? ? ? 說明:如果沒有依賴資源,可以使用該方法;如果有依賴資源,就會(huì)出現(xiàn)異常。當(dāng) targetPath = "prefabs/quad.prefab" 時(shí),創(chuàng)建的 Quad 顯示如下,Quad 顯示品紅,表示它依賴的材質(zhì)和圖片缺失。
? ? ? ? 2)加載有依賴的資源
????????LocalAssetLoader.cs?
using UnityEngine;public class LocalAssetLoader {// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compressprivate static string compressPath = Application.dataPath + "/AssetBundles/Compress"; // 壓縮文件根路徑// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/Compressprivate static string rootManifestPath = compressPath + "/Compress"; // 根manifest文件路徑(Compress.manifest文件絕對(duì)路徑)public static T LoadAsset<T>(string targetPath) { // 加載資源LoadDependencies(targetPath);return LoadTarget<T>(targetPath);}private static void LoadDependencies(string targetPath) { // 加載目標(biāo)資源的依賴AssetBundle manifestBundle = AssetBundle.LoadFromFile(rootManifestPath);// 解壓Manifest文件, 傳入的參數(shù)不區(qū)分大小寫(如果是大寫, 會(huì)自動(dòng)轉(zhuǎn)換為小寫)AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");string[] dependencies = manifest.GetAllDependencies(targetPath); // 獲取目標(biāo)文件的所有依賴for (int i = 0; i < dependencies.Length; i++) {string filePath = compressPath + "/" + dependencies[i];AssetBundle.LoadFromFile(filePath);}}private static T LoadTarget<T>(string targetPath) { // 加載目標(biāo)資源AssetBundle targetBundle = AssetBundle.LoadFromFile(compressPath + "/" + targetPath);string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);object obj = targetBundle.LoadAsset(fileName);if (obj != null) {return (T) obj;}return default(T);} }? ? ? ? 說明:manifest.GetAllDependencies()、AssetBundle.LoadFromFile()、targetBundle.LoadAsset() 的入?yún)⒉粎^(qū)分大小寫,因此傳入的 targetPath 也可以不區(qū)分大小寫。
? ? ? ? 3)調(diào)用 LocalAssetLoader 加載資源
????????SimpleLoad.cs
using UnityEngine;public class SimpleLoad : MonoBehaviour {private void Start() {GameObject obj = LocalAssetLoader.LoadAsset<GameObject>("prefabs/quad.prefab"); // 加載預(yù)設(shè)體// GameObject obj = LocalAssetLoader.LoadAsset<GameObject>("Prefabs/Quad.prefab"); // 加載預(yù)設(shè)體Instantiate(obj);} }? ? ? ? 說明:?由于 LocalAssetLoader.LoadAsset 的入?yún)⒉粎^(qū)分大小寫,因此傳入 "prefabs/quad.prefab" 和?"Prefabs/Quad.prefab" 都能正確加載資源。
? ? ? ? 運(yùn)行效果如下:
5 使用 WWW 加載服務(wù)器資源
????????W3AssetLoader.cs
using System; using System.Collections; using UnityEngine;public class W3AssetLoader : MonoBehaviour {private string compressPath; // 壓縮文件根路徑private string rootManifestPath; // 根manifest文件路徑private static W3AssetLoader instance; // 單例private void Awake() {instance = this;// compressPath = GetW3Path(Application.dataPath + "/AssetBundle/Compress");compressPath = "https://gitcode.net/m0_37602827/AssetBundleDemo/-/raw/master/Assets/AssetBundle/Compress";rootManifestPath = compressPath + "/Compress";}public static void LoadAsset<T>(string targetPath, Action<T> action) { // 加載資源instance.StartCoroutine(instance.LoadAssetCorutine<T>(targetPath, action));}private IEnumerator LoadAssetCorutine<T>(string targetPath, Action<T> action) { // 加載資源的協(xié)程yield return LoadDependencies(targetPath);yield return LoadTarget(targetPath, action);}private IEnumerator LoadDependencies(string targetPath) { // 加載目標(biāo)資源的依賴WWW w3 = WWW.LoadFromCacheOrDownload(rootManifestPath, 1);yield return w3;AssetBundle assetBundle = w3.assetBundle;if (assetBundle != null) {AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");if (manifest != null) {string[] dependencies = manifest.GetAllDependencies(targetPath); // 獲取目標(biāo)文件的所有依賴for (int i = 0; i < dependencies.Length; i++) {string filePath = compressPath + "/" + dependencies[i];w3 = WWW.LoadFromCacheOrDownload(filePath, 1);yield return w3;assetBundle = w3.assetBundle;}}}}private IEnumerator LoadTarget<T>(string targetPath, Action<T> action) { // 加載目標(biāo)文件string fullPath = compressPath + "/" + targetPath;WWW w3 = WWW.LoadFromCacheOrDownload(fullPath, 1);yield return w3;AssetBundle assetBundle = w3.assetBundle;if (assetBundle != null) {string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);object obj = assetBundle.LoadAsset(fileName); // 解壓文件if (obj != null && action != null) {action.Invoke((T) obj);}}}private string GetW3Path(string path) {#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WINpath = "file:///" + path;#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSXpath = "file://" + path;#endifreturn path;} }????????SimpleLoad.cs?
using UnityEngine;public class SimpleLoad : MonoBehaviour {private void Start() {W3AssetLoader.LoadAsset<GameObject>("prefabs/quad", Callback);// W3AssetLoader.LoadAsset<GameObject>("Prefabs/Quad", Callback);}private void Callback(GameObject obj) {Instantiate(obj);} }? ? ? ? 注意:W3AssetLoader.LoadAsset() 方法的入?yún)⒉灰?".prefab" 后綴,并且入?yún)⒁膊粎^(qū)分大小寫。
6 使用?UnityWebRequest 加載服務(wù)器資源
????????WebAssetLoader.cs
using System; using System.Collections; using UnityEngine; using UnityEngine.Networking;public class WebAssetLoader : MonoBehaviour {private string compressPath; // 壓縮文件根路徑private string rootManifestPath; // 根manifest文件路徑private static WebAssetLoader instance; // 單例private void Awake() {instance = this;// compressPath = GetW3Path(Application.dataPath + "/AssetBundle/Compress");compressPath = "https://gitcode.net/m0_37602827/AssetBundleDemo/-/raw/master/Assets/AssetBundle/Compress";rootManifestPath = compressPath + "/Compress";}public static void LoadAsset<T>(string targetPath, Action<T> action) { // 加載資源instance.StartCoroutine(instance.LoadAssetCorutine<T>(targetPath, action));}private IEnumerator LoadAssetCorutine<T>(string targetPath, Action<T> action) { // 加載資源的協(xié)程yield return LoadDependencies(targetPath);yield return LoadTarget(targetPath, action);}private IEnumerator LoadDependencies(string targetPath) { // 加載目標(biāo)資源的依賴UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(rootManifestPath);yield return webRequest.SendWebRequest();AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);if (assetBundle != null) {AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");if (manifest != null) {string[] dependencies = manifest.GetAllDependencies(targetPath); // 獲取目標(biāo)文件的所有依賴for (int i = 0; i < dependencies.Length; i++) {string filePath = compressPath + "/" + dependencies[i];webRequest = UnityWebRequestAssetBundle.GetAssetBundle(filePath);yield return webRequest.SendWebRequest();assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);}}}}private IEnumerator LoadTarget<T>(string targetPath, Action<T> action) { // 加載目標(biāo)文件string fullPath = compressPath + "/" + targetPath;UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(fullPath);yield return webRequest.SendWebRequest();AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);if (assetBundle != null) {string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);object obj = assetBundle.LoadAsset(fileName); // 解壓文件if (obj != null && action != null) {action.Invoke((T) obj);}}}private string GetW3Path(string path) {#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WINpath = "file:///" + path; // Windows平臺(tái)#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSXpath = "file://" + path; // Mac平臺(tái)#endifreturn path;} }????????SimpleLoad.cs?
using UnityEngine;public class SimpleLoad : MonoBehaviour {private void Start() {WebAssetLoader.LoadAsset<GameObject>("prefabs/quad.prefab", Callback);}private void Callback(GameObject obj) {Instantiate(obj);} }? ? ? ? 注意:WebAssetLoader.LoadAsset() 的入?yún)⑿枰砑?".prefab" 后綴,并且入?yún)^(qū)分大小寫。?
總結(jié)
以上是生活随笔為你收集整理的【Unity3D】基于AssetBundle实现资源热更新的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 智能手环core日志获取-兔盯云
- 下一篇: 实时游戏对战引擎MatchVS,我的对战