Unity资源Tree-资源加载

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[数据]

image:保存时的命名可以随便写自定义; image:包的hash image:_data是包数据

当该资源包有增量更新时,会在同名目录下新建新的hash包目录保存_data:

image, 4af开头就是最新的资源被保存了。

第二种方式用stream流自定义保存路径和信息

UnityWebRequest www = UnityWebRequest.Get(url);
byte[] datas = www.downloadHandler.data;
//然后开始操作datas
//如果有涉及到压缩解压
//stream流写入

批注 2020-08-06 160131

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黑盒内自动实例化的,很难追踪到那些依赖资源的实例对象。再说了,内存共享也会有大问题的!

卸载资源

原文地址:https://www.cnblogs.com/baolong-chen/p/13456205.html