1 基础API
1.1AssetBundle.LoadFromFile(Async)
LoadFromFile(Async)从本地存储(如硬盘或SD卡)加载未压缩或LZ4压缩格式的AssetBundle。API将只加载AssetBundle的头部,并将剩余的数据留在磁盘上。
AssetBundle的Objects会按需加载,比如:加载方法(例如:AssetBundle.Load)被调用或其InstanceID被间接引用的时候。在这种情况下,不会消耗过多的内存。但在Editor环境下,API还是会把整个AssetBundle加载到内存中,就像读取磁盘上的字节和使用AssetBundle.LoadFromMemoryAsync一样。
1.2 AssetBundle.LoadFromMemory(Async)
LoadFromMemory(Async) 是从托管代码的字节数组里加载AssetBundle。也就是说你要提前用其它的方式将资源的二进制数组加入到内存中。然后该接口会将源数据从托管代码字节数组复制到新分配的、连续的本机内存块中。如果AssetBundle使用了LZMA压缩类型,它将在复制时解压AssetBundle。而未压缩和LZ4压缩类型的AssetBundle将逐字节的完整复制。
此API消耗的最大内存量将至少是AssetBundle的两倍:本机内存中的一个副本,和LoadFromMemory(Async)从托管字节数组中复制的一个副本。
1.3 AssetBundle.LoadFromStream(Async)
LoadFromStream(Async)跟LoadFromFile通过filepath获得native stream,但区别就在于LoadFromFile内部经过封装能分段加载,而LoadFromStream的参数是一个托管的stream流,它会一次性将ab文件全部load进内存。
1.4 UnityWebRequest
request = UnityWebRequest.GetAssetBundle(); AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); //或者 request = UnityWebRequestAssetBundle.GetAssetBundle(); AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(uwr); //或者 request = UnityWebRequest.Get(); byte[] datas = request.downloadHandler.data; save2File(datas);//如果有LZMA压缩需要解压然后再压缩为LZ4
//也可以用Caching缓存
//或直接用CacheAssetBundle缓存,但要注意bundleName重复的问题
2 实施加载
2.1 更新资源
更新asset资源,获取服务器信息比对,有差异获取需要更新的文件:依赖清单、下载清单。依赖清单可以是AssetBundleManifest清单或者自定义的依赖清单,每次打包该文件都会改变,必须要更新;同时它也记录了所有包的依赖。下载清单就是它记录每个需要下载的ab包信息。
2.2 缓存资源
Unity可以用两种缓存方式:
第一种方式是用Unity封装好的Caching与UnityWebRequest.GetAssetBundle
Cache newCache = Caching.AddCache(version); Caching.currentCacheForWriting = newCache; UnityWebRequestAssetBundle.GetAssetBundle(url, CachedAssetBundle, [crc]);
//不加第二个参数CachedAssetBundle,就不会写入缓存
//同时LZMA压缩会自动解压并压缩为LZ4
缓存后的资源路径:persistentcubeMatf707096b238006af4f6898d647573409[数据]
:保存时的命名可以随便写自定义; :包的hash :_data是包数据
当该资源包有增量更新时,会在同名目录下新建新的hash包目录保存_data:
第二种方式用stream流自定义保存路径和信息
UnityWebRequest www = UnityWebRequest.Get(url); byte[] datas = www.downloadHandler.data; //然后开始操作datas //如果有涉及到压缩解压 //stream流写入
2.3 加载资源
Unity官网对加载本地资源时推荐API用AssetBundle.LoadFromFile(Async)接口,不推荐用UnityWebRequest。
缓存全局依赖信息,方便查询一个包的依赖。
为每个AssetBundle创建一个加载器,记录它包信息、依赖信息、加载状态。
加载队列不是一次全部加载,这里做了一个每帧最大加载个数,防止一次加载过多太卡。
加载完成,它的依赖对象引用计数++,它自身被实例化时引用计数也要++; 同时从加载队列中移除,加入到已加载完成的列表。
这里加载本地资源路径有两个:安装包路径、缓存路径(热更缓存)。
2.4 卸载资源
调用unload(false),会把包体的Serialize信息删除,但是内存中已加载的assets还存在,如果后续再次加载相同资源,内存中就会有两份。
对于使用这个卸载,一般都是对于不常用的功能模块,比如玩了许久才会用到该功能后续也不会立马用到,那么就可以先Unload(false),退出时再调用Resource.UnloadUnusedAsset();
调用unload(true),会把包体Serialize信息以及加载到内存的assets对象都要删除。调用之后,卸载的很干净,也是出现引用丢失的问题。再次加载时,游戏会卡顿:从磁盘加载Instantiate后,会实例化各assets对象,同时也会实例化scripAssets对象,然后执行所有的monoBehavior回调。
频繁使用的功能,不要调用Unload,就让他呆在内存,加速游戏体验。在某个合适的时机再卸载。
我也在想unload(false)后,能不能直接引用内存中的实例对象呢?我觉得不可能,加载克隆一个prefab,它的依赖资源(比如mesh、texture)都是在Unity黑盒内自动实例化的,很难追踪到那些依赖资源的实例对象。再说了,内存共享也会有大问题的!