当前位置:   article > 正文

AssetBundle详解_asset bundles文件

asset bundles文件

又来力,又是新的一篇总结性质的文章,这次接上篇热更新来讲一讲热更新过程中的重要角色AssetBundle,也就是ab包。

1. AB包概念

AssetBundle是将资源使用Unity提供的一种用于存储资源的压缩格式打包后的集合,它可以存储任何一种Unity可以识别的资源,如模型,纹理图,音频,场景等资源。也可以加载开发者自定义的二进制文件。他们的文件类型是.assetbundle/.unity3d,他们先前被设计好,很容易就下载到我们的游戏或者场景当中。

主要优点:AB包可以显著减少初始包体大小,在运行时动态加载卸载(即用即加载)

2. AB包的使用

首先,先讲讲游戏中AB包使用的大致流程:

  1. 创建Asset bundle,开发者在unity编辑器中通过脚本将所需要的资源打包成AssetBundle文件。
  2. 上传服务器,开发者将打包好的AssetBundle文件上传至服务器中。使得游戏客户端能够获取当前的资源,进行游戏的更新。
  3. 下载AssetBundle,首先将其下载到本地设备中,然后再通过AssetBundle的加载模块将资源加到游戏之中。
  4. 加载,通过Unity提供的API可以加载资源里面包含的模型、纹理图、音频、动画、场景等来更新游戏客户端。
  5. 卸载AssetBundle,卸载之后可以节省内存资源,并且要保证资源的正常更新。

可以看到,AB包的使用大致分为创建打包、下载、加载、卸载这四个主要步骤,接下来结合代码来详细讲讲这四个步骤具体实现。

2.1 创建并打包

注意,只有在Assets目录下的资源文件才可以用来创建AB包,在Unity中具体操作如下:
首先选中Assets/Materials/Black这个材质

在这里插入图片描述
接着可以在Inspector窗口最下面看到这样的界面,下边AssetBundle一行中左边是具体的AB包名(固定为小写),右边是定义后缀。
在这里插入图片描述
在定义好之后,接下来通过代码来进行AssetBundle打包。

示例:

BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
  • 1

打包过程中AB包可以通过设置BuildAssetBundleOptions来控制压缩方式,Unity共提供了三种压缩方式:

  • LZMA:默认的压缩方式,压缩包体相对小,解压时间相对长
  • LZ4:压缩包体相对大,解压时间相对短
  • 不压缩:压缩包体最大,访问速度最快

打包完成后会生成多个文件,具体到文中的例子来说会生成下面四个文件,其中.manifest文件可以看作是AB包的配置文件。其中AssetBundles包为主包,和父文件夹名相同。
在这里插入图片描述

2.2 下载

将AB包从服务器下载到本地的过程,这个可以合并入加载流程,具体细节留到后续再进行补充

2.3 加载

这个过程总共分为两步,首先需要先加载AB包,然后再从AB包中加载资源

2.3.1 加载AB包

  • AssetBundle.LoadFromMemory(Async optional)
  • AssetBundle.LoadFromStream(Async optional)
  • AssetBundle.LoadFromFile(Async optional)
  • UnityWebRequest’s DownloadHandlerAssetBundle
  • WWW.LoadFromCacheOrDownload (on Unity 5.6 or older)

1、一般来说,只要有可能,就应该使用AssetBundle.LoadFromFile。这个API在速度、磁盘使用和运行时内存使用方面是最有效的。
2、对于必须下载或热更新AssetBundles的项目,强烈建议对使用Unity5.3或更高版本的项目使用UnityWebRequest,对于使用Unity5.2或更老版本的项目使用WWW.LoadFromCacheOrDownload。
3、当使用UnityWebRequest或WWW.LoadFromCacheOrDownload时,要确保下载程序代码在加载AssetBundle后正确地调用Dispose。另外,C#的using语句是确保WWW或UnityWebRequest被安全处理的最方便的方法。
4、对于需要独特的、特定的缓存或下载需求的大项目,可以考虑使用自定义的下载器。编写自定义下载程序是一项重要并且复杂的任务,任何自定义的下载程序都应该与AssetBundle.LoadFromFile保持兼容
5、注意不能多次加载相同的AB包

2.3.2 从AB包中加载资源

具体到特定AB包的API

  • ab.LoadAllAssets<>() (Async optional)
  • ab.LoadAsset<>() (Async optional)
  • ab.LoadAssetWithSubAssets<>() (Async optional)

这些API的同步版本总是比异步版本快至少一个帧(其实是因为异步版本为了确保异步,都至少延迟了1帧),异步加载每帧会加载多个对象,直到他们的时间切片切出

2.3.3 AssetBundle依赖加载问题

具体情境:当A包中的obj1引用了B包中的obj2的时候,单独加载A包后再加载obj1会存在缺少相应的引用的问题,所以这个时候需要将B包也加载进来。注意,这里并不需要在加载A包之前提前加载B包,也不需要在B包中显式加载obj2,只需要在从A包中加载obj1之前加载B包即可。
常用的解决办法是通过主包的固定文件中的依赖信息来加载依赖包,具体代码如下:

//依赖加载问题
        //1.加载主包
        AssetBundle abMain = AssetBundle.LoadFromFile("AssetBundles/AssetBundles"); 
        //2.加载主包中的配置文件
        AssetBundleManifest abMainManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); 
        //3.从配置文件中获取AB包prefab的依赖信息
        var dependencies = abMainManifest.GetAllDependencies("prefab");
        //4.加载依赖包
        foreach (var str in dependencies)
        {
            AssetBundle.LoadFromFile("AssetBundles" + "/" + str);
        }
        //5.加载包prefab
        AssetBundle abPrefab = AssetBundle.LoadFromFile("AssetBundles/prefab");
        //6.从包prefab中加载相应资源
        var prefabs = abPrefab.LoadAllAssets<GameObject>();
        foreach (var prefab in prefabs)
            Instantiate(prefab);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.4 卸载

  • ab.UnLoad(bool):特定的ab包卸载
  • AssetBundle.UnloadAllAssetBundles(bool) : 卸载所有ab包

其中bool参数决定是否一并卸载从ab包中加载的资源文件,一般都是用false

2.5 AB包管理器

使用AB包管理器来进行统一管理
MonoSingleton.cs 泛型单例

//泛型单例
using UnityEngine;
using System;
namespace DefaultNamespace
{
    public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
    {
        private static T instance;

        public static T Instance
        {
            get
            {
                if (instance != null)
                    return instance;
                
                instance = FindObjectOfType<T>();
                if (instance == null)
                {
                    instance = new GameObject("Singleton " + typeof(T)).AddComponent<T>();
                    instance.Init();
                }
                return instance;
            }
        }
        
        //如果子类中有Awake则只会调用子类中的Awake,为方便统一管理,子类最好不要有Awake操作,需要其他初始化操作的话最好重写Init方法
        private void Awake()
        {
            
        }

        protected virtual void Init()
        {
            
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

ABMgr.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;

namespace DefaultNamespace
{
    public class ABMgr : MonoSingleton<ABMgr>
    {
        //主包
        private AssetBundle _mainAB;
        //主包配置文件 用于加载依赖包
        private AssetBundleManifest _mainABManifest;
        //字典 用来管理已加载的AB包
        private Dictionary<string, AssetBundle> _abCache;

        //AB包存放路径 根据平台来定
        private string BasePath
        {
            get
            {
#if UNITY_EDITOR || UNITY_STANDALONE
                return Application.streamingAssetsPath + "/";
#elif UNITY_IPHONE
                return "";
#elif UNITY_ANDROID
                return "";
#endif
            }
        }
        
        //主包名称 根据平台来定
        private string MainName
        {
            get
            {
#if UNITY_EDITOR || UNITY_STANDALONE
                return "StandaloneWindows";
#elif UNITY_IPHONE
                return "IOS";
#elif UNITY_ANDROID
                return "Android";
#endif
            }
        }

        protected override void Init()
        {
            base.Init();
            _abCache = new Dictionary<string, AssetBundle>();
        }

        #region 同步加载AB包

       public AssetBundle LoadAB(string abName)
          {
              AssetBundle ab;
              if (_mainAB == null)
              {
                  
                  _mainAB = AssetBundle.LoadFromFile(BasePath + MainName + "/" + "StandaloneWindows");
                  _mainABManifest = _mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
              }

              var dependencies = _mainABManifest.GetAllDependencies(abName);
              foreach (var name in dependencies)
              {
                  if (_abCache.ContainsKey(name))
                      continue;
                  //这里需要递归加载
                  LoadAB(name);
              }

              if (_abCache.ContainsKey(abName))
                  return _abCache[abName];
              else
              {
                  ab = AssetBundle.LoadFromFile(BasePath + MainName + "/" + abName);
                  _abCache.Add(abName, ab);
                  return ab;
              }
          }
              

        #endregion
        
        #region 同步加载资源
        /// <summary>
        /// 泛型加载资源
        /// </summary>
        /// <param name="abName">AB包名</param>
        /// <param name="assetName">资源名</param>
        public T LoadAsset<T>(string abName, string assetName) where T : Object
        {
            AssetBundle ab = LoadAB(abName);
            return ab.LoadAsset<T>(assetName);
        }
        
        //不指定类型 有重名情况下不建议使用 使用时需显示转换类型
        public Object LoadAsset(string abName,string resName)
        {
            AssetBundle ab = LoadAB(abName);
            return ab.LoadAsset(resName);
        }
        
        
        //利用参数传递类型,适合对泛型不支持的语言调用,使用时需强转类型
        public Object LoadAsset(string abName, string resName,System.Type type)
        {
            AssetBundle ab = LoadAB(abName);
            return ab.LoadAsset(resName,type);
        }
        #endregion
        
        #region 异步加载资源
        /// <summary>
        /// 泛型异步加载资源
        /// </summary>
        /// <param name="abName">AB包名</param>
        /// <param name="resName">资源名</param>
        /// <param name="OnComplete">完成回调</param>
        public void LoadAssetAsync<T>(string abName, string assetName, Action<Object> OnComplete) where T : Object
        {
            StartCoroutine(LoadAsset<T>(abName, assetName, OnComplete));
        }

        private IEnumerator LoadAsset<T>(string abName, string assetName, Action<Object> OnComplete) where T : Object
        {
            AssetBundle ab = LoadAB(abName);
            AssetBundleRequest abr = ab.LoadAssetAsync<T>(assetName);
            yield return abr;
            
            OnComplete(abr as T);
        }
        #endregion
        
        #region 卸载AB包
        //卸载单个AB包
        public void UnLoadAB(string abName)
        {
            if (_abCache.ContainsKey(abName))
            {
                _abCache[abName].Unload(false);
                _abCache.Remove(abName);
            }
        }
        //卸载所有AB包
        public void UnLoadAll()
        {
            AssetBundle.UnloadAllAssetBundles(false);
            _abCache.Clear();
            _mainAB = null;
            _mainABManifest = null;
        }
        
        #endregion
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159

参考:
Unity中AB包详解(超详细,特性,打包,加载,管理器)
Unity手游实战:从0开始SLG——资源管理系统-基础篇(三)AssetBundle原理
Unity热更新:AssetBundleBrowser打包及资源加载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/82222
推荐阅读
相关标签
  

闽ICP备14008679号