许可优化
产品
解决方案
服务支持
关于
软件库
当前位置:服务支持 >  软件文章 >  【Unity3D】基于AssetBundle实现资源热更新

【Unity3D】基于AssetBundle实现资源热更新

阅读数 5
点赞 0
article_banner

1 前言

        Unity3D 本地资源一般放在 Resources 目录下,但是 Resouces 文件夹的大小不能超过 2G,使用 AssetBundle 管理资源可以解决 Resources 文件夹受限问题。

        本文代码资源见→基于AssetBundle实现资源热更新(更新版)

        AssetBundle 主要用于管理资源,配合 AssetDatabase 和 AssetImporter 可以实现资源重命名,配合 BuildPipeline 可以实现资源压缩,配合 WWW 或 UnityWebRequest 可以实现加载服务器资源。下面简单介绍下相关接口:

        1)AssetBundle 获取资源名,加载、卸载资源

        静态方法: 

  1. // 从文件中加载AssetBundle
  2. public static AssetBundle LoadFromFile(string path)
  3. // 从二进制数组中加载AssetBundle
  4. public static AssetBundle LoadFromMemory(byte[] binary)
  5. // 从流中加载AssetBundle
  6. public static AssetBundle LoadFromStream(Stream stream)
  7. // 卸载所有AssetBundle
  8. public static void UnloadAllAssetBundles(bool unloadAllObjects)
  9. // 销毁对象
  10. public static void Destroy(Object obj)
cs
运行

        实例方法: 

  1. // 获取所有资源名
  2. public string[] GetAllAssetNames()
  3. // 判断是否包含资源
  4. public bool Contains(string name)
  5. // 加载资源
  6. public Object[] LoadAllAssets()
  7. public T[] LoadAllAssets<T>() where T : Object
  8. public Object[] LoadAllAssets(Type type)
  9. public Object LoadAsset(string name)
  10. public T LoadAsset<T>(string name) where T : Object
  11. public Object LoadAsset(string name, Type type)
  12. // 卸载资源, unloadAllLoadedObjects为false时不卸载已从Bundle中加载出的资源
  13. public void Unload(bool unloadAllLoadedObjects)
cs
运行

        说明:入参 name 不区分大小写,建议使用小写,如果使用大写会自动转换为小写。

        2)AssetBundleManifest 获取资源依赖

  1. // 获取所有AssetBundles
  2. public string[] GetAllAssetBundles()
  3. // 获取指定assetBundleName的直接依赖
  4. public string[] GetDirectDependencies(string assetBundleName)
  5. // 获取指定assetBundleName的所有依赖
  6. public string[] GetAllDependencies(string assetBundleName)
cs
运行

        说明:入参 assetBundleName 不区分大小写,建议使用小写,如果使用大写会自动转换为小写。 

        3)AssetDatabase 获取所有资源名、删除资源

  1. // 获取所有AssetBundle资源名
  2. public static string[] GetAllAssetBundleNames()
  3. // 根据assetBundleName删除AssetBundle
  4. public static bool RemoveAssetBundleName(string assetBundleName, bool forceRemove)
  5. // 刷新Project视图目录, 相当于右键手动刷新
  6. public static void Refresh()
cs
运行

        4)AssetImporter 设置资源名

  1. // 获取AssetImporter, 资源文件路径
  2. public static AssetImporter GetAtPath(string path)
  3. // 获取/设置资源文件名
  4. public string assetBundleName { get; set; }
cs
运行

        5)BuildPipeline 压缩资源

  1. // 压缩所有标记为AssetBundle的资源
  2. public static AssetBundleManifest BuildAssetBundles(
  3. string outputPath, // 压缩文件输出路径
  4. BuildAssetBundleOptions assetBundleOptions, // 压缩算法
  5. BuildTarget targetPlatform // 平台
  6. )
  7. // BuildAssetBundleOptions.None: LZMA压缩算法, 压缩比大, 加载慢, 使用前需要整体解压
  8. // BuildAssetBundleOptions.ChunkBasedCompression: LZ4压缩算法, 压缩比中等, 加载快可以加载指定资源而不用解压全部
  9. // BuildAssetBundleOptions.UncompressedAssetBundle: 不压缩, 加载快
cs
运行

        6)WWW 获取网络资源

  1. // 获取WWW
  2. public static WWW LoadFromCacheOrDownload(string url, int version)
  3. // 获取AssetBundle
  4. public AssetBundle assetBundle { get; }
cs
运行

        说明:WWW 被 Unity3D 官方标记为过时了,建议使用 UnityWebRequest。

         7)UnityWebRequest 获取网络资源

  1. UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri)
  2. yield return webRequest.SendWebRequest()
  3. AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest)
cs
运行

2 资源命名

        Asset 资源主要有脚本、图片、网格、 模型 、预设体等,在 Assets 窗口选中资源,在 Inspector 窗口选择 AssetBundle 下拉列表,选择 New 给资源添加 AssetBundle 名,如下:

        说明:AssetBundle 名不区分大小写,如果输入大写会自动转换为小写。只有添加了 AssetBundle 名的资源才能通过 BuildPipeline.BuildAssetBundles() 打包压缩

3 资源压缩

        1)创建目录及原资源 

        在 Assets 目录下创建 AssetBundles 目录(存放资源)和 Editor 目录(存放资源压缩脚本),在 AssetBundles 目录下创建 Compress 目录(存放压缩文件)和 Raw 目录(存放原资源文件),再在 Raw 目录下创建 Textures 目录(存放了一张图片 Picture)、Materials 目录(存放了一个材质 Material,并且依赖 Picture)、Prefabs 目录(存放了一个预设体 Quad,并且依赖 Material), 目录结构 如下:

        2)自动压缩脚本 

        AssetCompressor.cs

  1. using System.IO;
  2. using UnityEditor;
  3. using UnityEngine;
  4. public class AssetCompressor : Editor {
  5. // G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Raw
  6. private static string rawPath = Application.dataPath + "/AssetBundles/Raw";
  7. // G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress
  8. private static string compressPath = Application.dataPath + "/AssetBundles/Compress";
  9. [MenuItem("AssetBundle/CompressAssets")]
  10. public static void CompressAssets() { // 打包rawPath目录的资源, 生成压缩资源到compressPath目录
  11. ClearAllFilesBundleName();
  12. SetAssetBundlesName(rawPath);
  13. BuildAssetBundles();
  14. ClearAllFilesBundleName();
  15. AssetDatabase.Refresh(); // 刷新Project视图目录, 相当于右键手动刷新
  16. }
  17. private static void BuildAssetBundles() { // 压缩资源
  18. BuildPipeline.BuildAssetBundles(compressPath, // 压缩包输出包路径
  19. BuildAssetBundleOptions.ChunkBasedCompression, // 压缩算法
  20. BuildTarget.StandaloneWindows64 // Windows平台
  21. );
  22. }
  23. private static void ClearAllFilesBundleName() { // 删除所有AssetBundle名
  24. string[] names = AssetDatabase.GetAllAssetBundleNames();
  25. foreach (string name in names) {
  26. AssetDatabase.RemoveAssetBundleName(name, true);
  27. }
  28. }
  29. private static void SetAssetBundlesName(string rootPath) { // 设置资源的Bundle名
  30. DirectoryInfo rootInfo = new DirectoryInfo(rootPath);
  31. FileSystemInfo[] fileInfos = rootInfo.GetFileSystemInfos();
  32. foreach (FileSystemInfo fileInfo in fileInfos) {
  33. if (fileInfo is DirectoryInfo) {
  34. SetAssetBundlesName(fileInfo.FullName); // 递归遍历子文件夹下所有文件
  35. } else if (!fileInfo.Name.EndsWith(".meta")) {
  36. SetAssetBundleName(fileInfo.FullName);
  37. }
  38. }
  39. }
  40. private static void SetAssetBundleName(string filePath) { // 设置资源的Bundle名
  41. // 导入的相对路径(G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/prefabs/quad.prefab)
  42. string impoterPath = "Assets/" + filePath.Substring(Application.dataPath.Length + 1);
  43. AssetImporter assetImporter = AssetImporter.GetAtPath(impoterPath);
  44. if (assetImporter != null) {
  45. filePath = filePath.Substring(rawPath.Length + 1); // 去源文件前缀(可选, 建议使用)
  46. // filePath = filePath.Substring(filePath.LastIndexOf("\\") + 1); // 去所有前缀(可选, 不建议使用)
  47. // 去后缀(可选, 不去后缀刷新目录后会报错, 但不影响资源压缩和后续资源加载)
  48. filePath = filePath.Remove(filePath.LastIndexOf("."));
  49. assetImporter.assetBundleName = filePath;
  50. }
  51. }
  52. }
cs
运行

         说明:AssetCompressor.cs 文件需要放在 Editor 目录下,编译成功后,在菜单栏可以看到 AssetBundle 菜单,如下:

         点击 CompressAssets 选项,会将 Assets/AssetBundles/Raw 目录下的资源打包压缩至 Assets/AssetBundles/Compress 目录,如下:

         注意:如果压缩名不去后缀,会报以下错误,这是因为文件已经压缩了,但还是以 “.prefab”、“.jpg”、“.mat” 为后缀,被 Unity3D 识别为损坏文件。该错误不影响压缩文件生成,也不影响后续资源加载,可以忽略。如果不想出现以下报错,可以将去后缀的注释代码打开。

         3)压缩文件

         打开 Compress.manifest 文件如下:

  1. ManifestFileVersion: 0
  2. CRC: 2688481811
  3. AssetBundleManifest:
  4. AssetBundleInfos:
  5. Info_0:
  6. Name: materials/material
  7. Dependencies:
  8. Dependency_0: textures/picture
  9. Info_1:
  10. Name: prefabs/quad
  11. Dependencies:
  12. Dependency_0: materials/material
  13. Info_2:
  14. Name: textures/picture
  15. Dependencies: {}
cs
运行

         说明:后续要加载资源时,如果不清楚 AssetBundle 名,可以在 Compress.manifest 文件中查看相应 Name 值。可以看到,这里的 Name 值也全都自动转换为小写了,在加载资源时,如果传入大写的也能正常获取到相应资源

4 加载本地资源

        1)加载简单资源

  1. // targetPath="ptextures/picture"
  2. public static T LoadAsset<T>(string targetPath) { // 加载资源
  3. // G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/ptextures/picture
  4. AssetBundle targetBundle = AssetBundle.LoadFromFile(compressPath + "/" + targetPath);
  5. string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1); // picture.jpg
  6. object obj = targetBundle.LoadAsset(fileName);
  7. if (obj != null) {
  8. return (T) obj;
  9. }
  10. return default(T);
  11. }
cs
运行

        说明:如果没有依赖资源,可以使用该方法;如果有依赖资源,就会出现异常。当 targetPath = "prefabs/quad" 时,创建的 Quad 显示如下,Quad 显示品红,表示它依赖的材质和图片缺失。

        2)加载有依赖的资源

        LocalAssetLoader.cs 

  1. using UnityEngine;
  2. public class LocalAssetLoader {
  3. // G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress
  4. private static string compressPath = Application.dataPath + "/AssetBundles/Compress"; // 压缩文件根路径
  5. // G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/Compress
  6. private static string rootManifestPath = compressPath + "/Compress"; // 根manifest文件路径(Compress.manifest文件绝对路径)
  7. public static T LoadAsset<T>(string targetPath) { // 加载资源
  8. LoadDependencies(targetPath);
  9. return LoadTarget<T>(targetPath);
  10. }
  11. private static void LoadDependencies(string targetPath) { // 加载目标资源的依赖
  12. AssetBundle manifestBundle = AssetBundle.LoadFromFile(rootManifestPath);
  13. // 解压Manifest文件, 传入的参数不区分大小写(如果是大写, 会自动转换为小写)
  14. AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
  15. string[] dependencies = manifest.GetAllDependencies(targetPath); // 获取目标文件的所有依赖
  16. for (int i = 0; i < dependencies.Length; i++) {
  17. string filePath = compressPath + "/" + dependencies[i];
  18. AssetBundle.LoadFromFile(filePath);
  19. }
  20. }
  21. private static T LoadTarget<T>(string targetPath) { // 加载目标资源
  22. AssetBundle targetBundle = AssetBundle.LoadFromFile(compressPath + "/" + targetPath);
  23. string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);
  24. object obj = targetBundle.LoadAsset(fileName);
  25. if (obj != null) {
  26. return (T) obj;
  27. }
  28. return default(T);
  29. }
  30. }
cs
运行

        说明:manifest.GetAllDependencies()、AssetBundle.LoadFromFile()、targetBundle.LoadAsset() 的入参不区分大小写,因此传入的 targetPath 也可以不区分大小写。

        3)调用 LocalAssetLoader 加载资源

        SimpleLoad.cs

  1. using UnityEngine;
  2. public class SimpleLoad : MonoBehaviour {
  3. private void Start() {
  4. GameObject obj = LocalAssetLoader.LoadAsset<GameObject>("prefabs/quad"); // 加载预设体
  5. // GameObject obj = LocalAssetLoader.LoadAsset<GameObject>("Prefabs/Quad"); // 加载预设体
  6. Instantiate(obj);
  7. }
  8. }
cs
运行

        说明: 由于 LocalAssetLoader.LoadAsset 的入参不区分大小写,因此传入 "prefabs/quad" 和 "Prefabs/Quad" 都能正确加载资源。

        运行效果如下:

5 使用 WWW 加载服务器资源

        W3AssetLoader.cs

  1. using System;
  2. using System.Collections;
  3. using UnityEngine;
  4. public class W3AssetLoader : MonoBehaviour {
  5. private string compressPath; // 压缩文件根路径
  6. private string rootManifestPath; // 根manifest文件路径
  7. private static W3AssetLoader instance; // 单例
  8. private void Awake() {
  9. instance = this;
  10. // compressPath = GetW3Path(Application.dataPath + "/AssetBundle/Compress");
  11. compressPath = "https://gitcode.net/m0_37602827/AssetBundleDemo/-/raw/master/Assets/AssetBundles/Compress";
  12. rootManifestPath = compressPath + "/Compress";
  13. }
  14. public static void LoadAsset<T>(string targetPath, Action<T> action) { // 加载资源
  15. instance.StartCoroutine(instance.LoadAssetCorutine<T>(targetPath, action));
  16. }
  17. private IEnumerator LoadAssetCorutine<T>(string targetPath, Action<T> action) { // 加载资源的协程
  18. yield return LoadDependencies(targetPath);
  19. yield return LoadTarget(targetPath, action);
  20. }
  21. private IEnumerator LoadDependencies(string targetPath) { // 加载目标资源的依赖
  22. WWW w3 = WWW.LoadFromCacheOrDownload(rootManifestPath, 1);
  23. yield return w3;
  24. AssetBundle assetBundle = w3.assetBundle;
  25. if (assetBundle != null) {
  26. AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
  27. if (manifest != null) {
  28. string[] dependencies = manifest.GetAllDependencies(targetPath); // 获取目标文件的所有依赖
  29. for (int i = 0; i < dependencies.Length; i++) {
  30. string filePath = compressPath + "/" + dependencies[i];
  31. w3 = WWW.LoadFromCacheOrDownload(filePath, 1);
  32. yield return w3;
  33. assetBundle = w3.assetBundle;
  34. }
  35. }
  36. }
  37. }
  38. private IEnumerator LoadTarget<T>(string targetPath, Action<T> action) { // 加载目标文件
  39. string fullPath = compressPath + "/" + targetPath;
  40. WWW w3 = WWW.LoadFromCacheOrDownload(fullPath, 1);
  41. yield return w3;
  42. AssetBundle assetBundle = w3.assetBundle;
  43. if (assetBundle != null) {
  44. string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);
  45. object obj = assetBundle.LoadAsset(fileName); // 解压文件
  46. if (obj != null && action != null) {
  47. action.Invoke((T) obj);
  48. }
  49. }
  50. }
  51. private string GetW3Path(string path) {
  52. #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
  53. path = "file:///" + path;
  54. #elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
  55. path = "file://" + path;
  56. #endif
  57. return path;
  58. }
  59. }
cs
运行

        SimpleLoad.cs 

  1. using UnityEngine;
  2. public class SimpleLoad : MonoBehaviour {
  3. private void Start() {
  4. W3AssetLoader.LoadAsset<GameObject>("prefabs/quad.prefab", Callback);
  5. // W3AssetLoader.LoadAsset<GameObject>("Prefabs/Quad.prefab", Callback);
  6. }
  7. private void Callback(GameObject obj) {
  8. Instantiate(obj);
  9. }
  10. }
cs
运行

        说明:W3AssetLoader.LoadAsset() 方法的入参不区分大小写。

6 使用 UnityWebRequest 加载服务器资源

        WebAssetLoader.cs

  1. using System;
  2. using System.Collections;
  3. using UnityEngine;
  4. using UnityEngine.Networking;
  5. public class WebAssetLoader : MonoBehaviour {
  6. private string compressPath; // 压缩文件根路径
  7. private string rootManifestPath; // 根manifest文件路径
  8. private static WebAssetLoader instance; // 单例
  9. private void Awake() {
  10. instance = this;
  11. // compressPath = GetW3Path(Application.dataPath + "/AssetBundle/Compress");
  12. compressPath = "https://gitcode.net/m0_37602827/AssetBundleDemo/-/raw/master/Assets/AssetBundles/Compress";
  13. rootManifestPath = compressPath + "/Compress";
  14. }
  15. public static void LoadAsset<T>(string targetPath, Action<T> action) { // 加载资源
  16. instance.StartCoroutine(instance.LoadAssetCorutine<T>(targetPath, action));
  17. }
  18. private IEnumerator LoadAssetCorutine<T>(string targetPath, Action<T> action) { // 加载资源的协程
  19. yield return LoadDependencies(targetPath);
  20. yield return LoadTarget(targetPath, action);
  21. }
  22. private IEnumerator LoadDependencies(string targetPath) { // 加载目标资源的依赖
  23. UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(rootManifestPath);
  24. yield return webRequest.SendWebRequest();
  25. AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);
  26. if (assetBundle != null) {
  27. AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
  28. if (manifest != null) {
  29. string[] dependencies = manifest.GetAllDependencies(targetPath); // 获取目标文件的所有依赖
  30. for (int i = 0; i < dependencies.Length; i++) {
  31. string filePath = compressPath + "/" + dependencies[i];
  32. webRequest = UnityWebRequestAssetBundle.GetAssetBundle(filePath);
  33. yield return webRequest.SendWebRequest();
  34. assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);
  35. }
  36. }
  37. }
  38. }
  39. private IEnumerator LoadTarget<T>(string targetPath, Action<T> action) { // 加载目标文件
  40. string fullPath = compressPath + "/" + targetPath;
  41. UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(fullPath);
  42. yield return webRequest.SendWebRequest();
  43. AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);
  44. if (assetBundle != null) {
  45. string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);
  46. object obj = assetBundle.LoadAsset(fileName); // 解压文件
  47. if (obj != null && action != null) {
  48. action.Invoke((T) obj);
  49. }
  50. }
  51. }
  52. private string GetW3Path(string path) {
  53. #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
  54. path = "file:///" + path; // Windows平台
  55. #elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
  56. path = "file://" + path; // Mac平台
  57. #endif
  58. return path;
  59. }
  60. }
cs
运行

        SimpleLoad.cs 

  1. using UnityEngine;
  2. public class SimpleLoad : MonoBehaviour {
  3. private void Start() {
  4. WebAssetLoader.LoadAsset<GameObject>("prefabs/quad", Callback);
  5. }
  6. private void Callback(GameObject obj) {
  7. Instantiate(obj);
  8. }
  9. }
cs
运行

        说明:WebAssetLoader.LoadAsset() 的入参区分大小写。 


免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删
相关文章
QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空