Unity AssetBundle 加载、引用计数实现

news/2024/11/29 20:41:43/

 首先,创建一个名为AssetBundleManager的脚本,用于管理 AssetBundle 的打包、加载和引用计数:

using UnityEngine;public class AssetBundleManager : MonoBehaviour
{private static AssetBundleManager instance;private static AssetBundleManifest manifest;private static string assetBundleDirectory;// 已加载的 AssetBundle 字典private static Dictionary<string, AssetBundle> loadedAssetBundles = new Dictionary<string, AssetBundle>();// AssetBundle 引用计数字典private static Dictionary<string, int> assetBundleRefCount = new Dictionary<string, int>();public static AssetBundleManager Instance{get{if (instance == null){GameObject go = new GameObject("AssetBundleManager");instance = go.AddComponent<AssetBundleManager>();}return instance;}}// 初始化 AssetBundleManagerpublic void Initialize(string manifestPath, string assetBundleFolderPath){manifest = LoadAssetBundleManifest(manifestPath);assetBundleDirectory = assetBundleFolderPath;}// 加载指定 AssetBundle 中的资源public T LoadAsset<T>(string assetBundleName, string assetName) where T : UnityEngine.Object{AssetBundle assetBundle = LoadAssetBundle(assetBundleName);if (assetBundle != null){return assetBundle.LoadAsset<T>(assetName);}return null;}// 卸载 AssetBundlepublic void UnloadAssetBundle(string assetBundleName){if (loadedAssetBundles.ContainsKey(assetBundleName)){AssetBundle assetBundle = loadedAssetBundles[assetBundleName];assetBundle.Unload(false);loadedAssetBundles.Remove(assetBundleName);if (assetBundleRefCount.ContainsKey(assetBundleName)){assetBundleRefCount[assetBundleName]--;if (assetBundleRefCount[assetBundleName] <= 0){assetBundleRefCount.Remove(assetBundleName);}}}}// 加载 AssetBundleprivate AssetBundle LoadAssetBundle(string assetBundleName){if (!loadedAssetBundles.ContainsKey(assetBundleName)){string path = Path.Combine(assetBundleDirectory, assetBundleName);AssetBundle assetBundle = AssetBundle.LoadFromFile(path);if (assetBundle != null){loadedAssetBundles.Add(assetBundleName, assetBundle);}}if (loadedAssetBundles.ContainsKey(assetBundleName)){if (!assetBundleRefCount.ContainsKey(assetBundleName)){assetBundleRefCount.Add(assetBundleName, 1);}else{assetBundleRefCount[assetBundleName]++;}return loadedAssetBundles[assetBundleName];}return null;}// 加载 AssetBundleManifestprivate AssetBundleManifest LoadAssetBundleManifest(string manifestPath){AssetBundle manifestAssetBundle = AssetBundle.LoadFromFile(manifestPath);return manifestAssetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");}
}

接下来,创建一个名为BuildEditor的脚本,用于打包指定目录中的资源到 AssetBundle:

using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;public class BuildEditor : EditorWindow
{
#if UNITY_ANDROIDstatic string m_CurPlatformName = "Android";
#elif UNITY_IOSstatic string m_CurPlatformName = "IOS";
#elsestatic string m_CurPlatformName = "Windows";
#endif/// <summary>/// 资源打包路径/// </summary>private static string m_OutPath = Application.dataPath + "/GameRes";/// <summary>/// 当前选择的平台/// </summary>private static BuildTarget m_BuildTarget = EditorUserBuildSettings.activeBuildTarget;/// <summary>/// 资源输出路径/// </summary>private static string m_BundlePutPath = Application.streamingAssetsPath + "/" + m_CurPlatformName + "/AssetBundles";/// <summary>/// 打包列表/// </summary>private static List<string> finalFiles = new List<string>();/// <summary>/// 打包完成之后生成的Manifest文件/// </summary>private static AssetBundleManifest m_Manifest;/// <summary>/// 打包/// </summary>[MenuItem("Build/Build ab")]private static void Build(){finalFiles.Clear();//1.清除AssetBundleNameClearAssetBundleName();//2.设置AssetBundleNamePack(m_OutPath);//3.打包if (Directory.Exists(m_BundlePutPath)) Directory.Delete(m_BundlePutPath, true);string tempFilePath = m_BundlePutPath;Directory.CreateDirectory(m_BundlePutPath);m_Manifest = BuildPipeline.BuildAssetBundles(m_BundlePutPath, BuildAssetBundleOptions.DeterministicAssetBundle, m_BuildTarget);if (m_Manifest != null){DeleteManifestFile(m_BundlePutPath);CreateFileList();this.BuildSuccessOrFail("Build AB", "Build Succeed", "OK");}else{this.BuildSuccessOrFail("Build AB", "Build Fail", "OK");}EditorUtility.ClearProgressBar();AssetDatabase.Refresh();Debug.Log("Build Succeed !!!");}/// <summary>/// 清除AssetBundle/// </summary>public static void ClearAssetBundleName(){string[] strs = AssetDatabase.GetAllAssetBundleNames();foreach (var bundleName in strs){AssetDatabase.RemoveAssetBundleName(bundleName, true);}AssetDatabase.Refresh();}/// <summary>/// 检查文件/// </summary>/// <param name="path"></param>private static void Pack(string path){DirectoryInfo infos = new DirectoryInfo(path);FileSystemInfo[] files = infos.GetFileSystemInfos();for (int i = 0; i < files.Length; i++){if (files[i] is DirectoryInfo){Pack(files[i].FullName);}else{if (!files[i].FullName.EndsWith(".meta")){SetAssetBundleName(files[i].FullName);}}}}/// <summary>/// 删除掉当前路径下所有.Manifest文件/// </summary>/// <param name="path"></param>private static void DeleteManifestFile(string path){DirectoryInfo infos = new DirectoryInfo(path);FileSystemInfo[] files = infos.GetFileSystemInfos();for (int i = 0; i < files.Length; i++){if (files[i] is DirectoryInfo){DeleteManifestFile(files[i].FullName);}else{if (files[i].FullName.EndsWith(".manifest")){File.Delete(files[i].FullName);}}}}/// <summary>/// 设置AssetBundleName/// </summary>/// <param name="source"></param>private static void SetAssetBundleName(string source){string _source = source.Replace(@"\\", "/");//截取完整路径到Assets/位置获得Assets路径string _assetPath1 = "Assets" + _source.Substring(Application.dataPath.Length);//获取Assets/之后的路径 以方便做AssetBunndleName使用string _assetPath2 = _source.Substring(Application.dataPath.Length + 1);//获取Assets路径下的文件AssetImporter assetImporter = AssetImporter.GetAtPath(_assetPath1);//获取AssetBundleNamestring bundleName = _assetPath2;//获取文件的扩展名并将其替换成.assetbundlebundleName = bundleName.Replace(Path.GetExtension(bundleName), ".assetbundle");//设置Bundle名assetImporter.assetBundleName = bundleName;//获取每个文件的指定路径并添加到列表里以方便写file文件使用string newFilePath = m_BundlePutPath + PathUtils.GetRelativePath(source, Application.dataPath, "").ToLower();string[] strs = newFilePath.Split('.');finalFiles.Add(strs[0] + ".assetbundle");}/// <summary>/// 生成 file 文件/// </summary>static void CreateFileList(){//files文件 目标路径string targetFilePath = m_BundlePutPath + "/files.txt";//检查是否存在该文件 存在则删除if (File.Exists(targetFilePath))File.Delete(targetFilePath);//统计大小 单位Blong totalFileSize = 0;FileStream fs = new FileStream(targetFilePath, FileMode.CreateNew);StreamWriter sw = new StreamWriter(fs);int count = 0;string file;finalFiles.Add(m_BundlePutPath + "/AssetBundles");File.Delete(m_BundlePutPath + "/AssetBundles.manifest");m_BundlePutPath = m_BundlePutPath.Replace('\\', '/');foreach (var files in finalFiles){file = PathUtils.NormalizePath(files);count++;this.UpdateProgress(count, finalFiles.Count + 1, "Createing files.txt");//文件Hashstring hash = "";//取到文件路径string _path = file.Replace(m_BundlePutPath + "/", string.Empty);FileInfo fi = new FileInfo(file);if (Path.GetExtension(file) == ".assetbundle"){//取到文件在Manifest中引用名字string abname = file.Replace(m_BundlePutPath + "/", "");//通过引用名字去Manifest文件中获取到该文件的Hash值hash = m_Manifest.GetAssetBundleHash(abname).ToString();}else{hash = this.md5file(file);}totalFileSize += fi.Length;//将文件信息按行写入到files文件中 路径|Hash|文件大小(单位B)sw.WriteLine(_path + "|" + hash + "|" + fi.Length);}//最后写入总大小(单位B)sw.WriteLine("" + totalFileSize);sw.Close();fs.Close();}/// <summary>/// 计算文件的MD5值/// </summary>public string md5file(string file){try{FileStream fs = new FileStream(file, FileMode.Open);System.Security.Cryptography.MD5 md5 = new         System.Security.Cryptography.MD5CryptoServiceProvider();byte[] retVal = md5.ComputeHash(fs);fs.Close();StringBuilder sb = new StringBuilder();for (int i = 0; i < retVal.Length; i++){sb.Append(retVal[i].ToString("x2"));}return sb.ToString();}catch (Exception ex){throw new Exception("md5file() fail, error:" + ex.Message);}}public void BuildSuccessOrFail(string tittle,string message,string okmsg){EditorUtility.DisplayDialog(tittle, message, okmsg);}/// <summary>/// 进度条更新/// </summary>public void UpdateProgress(int progress, int progressMax, string desc){string title = "Processing...[" + progress + " - " + progressMax + "]";float value = (float)progress / (float)progressMax;EditorUtility.DisplayProgressBar(title, desc, value);}
}

使用示例 

using UnityEngine;public class AssetBundleTest : MonoBehaviour
{private void Start(){// 初始化 AssetBundleManager,传入 AssetBundleManifest 路径和 AssetBundle 文件夹路径string manifestPath = "路径/manifest";string assetBundleFolderPath = "路径/AssetBundles";AssetBundleManager.Instance.Initialize(manifestPath, assetBundleFolderPath);// 加载 AssetBundle 中的资源string assetBundleName = "myassets";string assetName = "MyPrefab";GameObject myPrefab = AssetBundleManager.Instance.LoadAsset<GameObject>(assetBundleName, assetName);if (myPrefab != null){// 使用资源Instantiate(myPrefab);}else{Debug.LogError("Failed to load asset from AssetBundle: " + assetBundleName);}// 卸载 AssetBundleAssetBundleManager.Instance.UnloadAssetBundle(assetBundleName);}
}

 

注解:

  1. AssetBundleManagerAssetBundleBuilder脚本放入 Unity 项目中的任意目录中。
  2. 选择要打包成 AssetBundle 的资源文件夹。
  3. 在 Unity 编辑器中,点击菜单栏的Assets,然后选择Build AssetBundles
  4. AssetBundle 将会被打包到 StreamingAssets 文件夹中。
  5. 在需要加载 AssetBundle 的地方,通过AssetBundleManager.Instance.LoadAsset<T>(assetBundleName, assetName)来加载指定 AssetBundle 中的资源。
    • assetBundleName是 AssetBundle 文件的名称(不带后缀)。
    • assetName是要加载的资源名称。
  6. 加载完毕后,使用资源并在不再需要时调用AssetBundleManager.Instance.UnloadAssetBundle(assetBundleName)来卸载 AssetBundle。

请注意,具体的路径和资源名称应根据你的实际情况进行修改。此外,为了更好地管理资源,你可能还需要额外的代码来处理资源的引用以及资源的释放。


http://www.ppmy.cn/news/564696.html

相关文章

Integer移位算法

常用移位算法 给定值最高位1的权重给定值最低位1的权重给定值高位连续零的个数给定值低位连续零个数 给定值最高位1的权重 也就是给定值左侧。返回给定值左侧最大的2的次幂 //获取i最高位1代表的2次幂&#xff0c;最高位1代表的权值public static int highestOneBit(int i) …

浅谈单线程和多线程的异同

前两天有个面试&#xff0c;面试官问了我一个单线程和多线程的问题&#xff0c;情境如下&#xff1a; 面试官&#xff1a;你对单线程和多线程有什么看法&#xff1f; 我&#xff1a; 面试官&#xff1a; 我&#xff1a; 面试官&#xff1a; 我 现在先让我们来了解一下进程…

智慧渲染,通用为“先”---- 象帝先推动 “通用、好用、高性能、自主可控” GPU解决方案

市场需要通用GPU GPU是计算机系统的必需组件&#xff0c;而且随着应用场景的多样化&#xff0c;GPU已经成为智算时代最为重要的生产力组件。一颗标准的GPU不仅能满足日常的桌面显示、图形渲染功能&#xff0c;而且可以提供AI智算的加速能力&#xff0c;同时对功耗与发热的要求…

说说你对 SSG 的理解

说说你对 SSG 的理解 SSG&#xff08;Static Site Generation&#xff0c;静态网站生成&#xff09;&#xff09;是一种构建静态网站的技术和方法&#xff0c;在构建时预先生成静态页面&#xff0c;并将这些页面部署到 CDN 或者其他存储服务中&#xff0c;以提升 Web 应用的性…

PotPlayer快进时间出错

PotPlayer自己明明设置的是每次快进/快退1秒&#xff0c;但是却每次调整的时间都大于1秒&#xff1f; 将播放-时间跨度里面的 如存在关键帧数据则以关键帧为移动单位 的前面的√来去掉即可。 去掉之后再调节就正常了。

使用potplayer播放器看直播

1、打开potplayer播放器。 2、点击三条杠&#xff0c;弹出播放列表&#xff0c;如图点击。 3、新建专辑&#xff0c;输入名称&#xff0c;歪脖播放列表中选择地址&#xff0c;输入地址后按确定&#xff0c;再确定&#xff0c;如图。

Potplayer播放器“打开链接”卡住的解决方法

最近装了个potplayer用来看电视直播,每次打开链接都会假死几秒钟,去百度找了几个方法适用后只有这一个有效,记录并分享一下. 1、下载MediaUrlList - 解决【打开链接】卡顿(推荐).as&#xff0c; 下载地址&#xff1a; 链接: https://pan.baidu.com/s/1mhhlo1UwQ9FVmlKhmWnysA …

手机快播安卓版 Qvod player V1.0.19

快播播放器(Qvod Player)是一款基于准视频点播(QVOD)内核的,集在线点播、在线直播及本地播放于一体的全能播放器。快播运用自主研发的QVOD 流媒体传输协议,率先实现了高清视频在互联网络的流畅播放。快播支持RMVB、MPEG、AVI、WMV等主流音视频格式,具有资源占用低、操作简捷、运…