专注软件优化分享的网站,wordpress提示窗,网站建设属于技术开发合同吗,台州高端网站建设先用一句话介绍一下AssetBundle吧。AssetBundle系统提供了一种压缩文件的格式#xff0c;可以把1到多个文件进行索引和序列化。Unity项目在交付安装之后#xff0c;会通过AssetBundle对不包含代码的资源进行更新。这就允许开发人员先提交一个小的应用程序包#xff0c;将运行…先用一句话介绍一下AssetBundle吧。AssetBundle系统提供了一种压缩文件的格式可以把1到多个文件进行索引和序列化。Unity项目在交付安装之后会通过AssetBundle对不包含代码的资源进行更新。这就允许开发人员先提交一个小的应用程序包将运行时内存压力降到最低并有选择地加载针对不同终端用户设备优化后的内容。3.1 AssetBundle结构总的来说AssetBundle就像传统的压缩包一样由两个部分组成包头和数据段。包头包含有关AssetBundle 的信息比如标识符、压缩类型和内容清单。清单是一个以Objects name为键的查找表。每个条目都提供一个字节索引用来指示该Objects在AssetBundle数据段的位置。在大多数平台上这个查找表是用平衡搜索树实现的。具体来说Windows和OSX派生平台(包括IOS)都采用了红黑树。因此构建清单所需的时间会随着AssetBundle中Assets的数量增加而线性增加。数据段包含通过序列化AssetBundle中的Assets而生成的原始数据。如果指定LZMA为压缩方案的话则对所有序列化Assets后的完整字节数组进行压缩。如果指定了LZ4则单独压缩单独Assets的字节。如果不使用压缩数据段将保持为原始字节流。在Unity5.3之前是无法对AssetBundle中单独Objects进行压缩的。因此如果在5.3之前的Unity版本被要求从压缩的AssetBundle中读取一个或多个对象时Unity必须解压整个AssetBundle。通常Unity会缓存AssetBundle的解压缩副本以提高在相同AssetBundle上的后续加载请求的加载性能。3.2 加载AssetBundlesAssetBundles可以通过四个不同的API进行加载。但受限于两个标准这四个API的行为是不同的。2个标准如下1、AssetBundles的压缩方式LZMA、LZ4、还是未压缩的。2、AssetBundles的加载平台。而四个API分别是AssetBundle.LoadFromMemory(Async optional)AssetBundle.LoadFromFile(Async optional)UnityWebRequests DownloadHandlerAssetBundleWWW.LoadFromCacheOrDownload (on Unity 5.6 or older)下面来详细讲一下4个API的区别。3.2.1 AssetBundle.LoadFromMemory(Async)Unity的建议是 不要使用这个API LoadFromMemory(Async) 是从托管代码的字节数组里加载AssetBundle。也就是说你要提前用其他的方式将资源的二进制数组加入到内存中。然后该接口会将源数据从托管代码字节数组复制到新分配的、连续的本机内存块中。但如果AssetBundle使用了LZMA压缩类型它将在复制时解压AssetBundle。而未压缩和LZ4压缩类型的AssetBundle将逐字节的完整复制。之所以不建议使用该API是因为此API消耗的最大内存量将至少是AssetBundle的两倍本机内存中的一个副本和LoadFromMemory(Async)从托管字节数组中复制的一个副本。因此从通过此API创建的AssetBundle加载的资产将在内存中冗余三次一次在托管代码字节数组中一次在AssetBundle的栈内存副本中第三次在GPU或系统内存中用于Asset本身。注意在Unity5.3.3之前这个API被称为AssetBundle.CreateFromMemory。但功能没有改变。3.2.2 AssetBundle.LoadFromFile(Async)LoadFromFile是一种高效的API用于从本地存储(如硬盘或SD卡)加载未压缩或LZ4压缩格式的AssetBundle。在桌面独立平台、控制台和移动平台上API将只加载AssetBundle的头部并将剩余的数据留在磁盘上。AssetBundle的Objects 会按需加载比如加载方法(例如AssetBundle.Load)被调用或其InstanceID被间接引用的时候。在这种情况下不会消耗过多的内存。但在Editor环境下中API还是会把整个AssetBundle加载到内存中就像读取磁盘上的字节和使用AssetBundle.LoadFromMemoryAsync一样。如果在Editor中对项目进行了分析此API可能会导致在AssetBundle加载期间出现内存尖峰。但这不应影响设备上的性能在做优化之前这些尖峰应该在设备上重新再测试一一遍.要注意这个API只针对未压缩或LZ4压缩格式因为前面说过了如果使用LZMA压缩的话它是针对整个生成后的数据包进行压缩的所以在未解压之前是无法拿到AssetBundle的头信息的。注意这里曾经有过一个历史遗留问题即在Unity5.3或更老版本的Android设备上当试图从Streaming Assets路径加载AssetBundles时此API将失败。这个问题已在Unity5.4中已经解决。在Unity5.3之前这个API被称为AssetBundle.CreateFromFile。其功能没有改变。3.2.3 AssetBundleDownloadHandlerDownloadHandlerAssetBundle的操作是通过UnityWebRequest的API来完成的。UnityWebRequest API允许开发人员精确地指定Unity 应如何处理下载的数据并允许开发人员消除不必要的内存使用。使用UnityWebRequest下载AssetBundle 的最简单方法是调用UnityWebRequest.GetAssetBundle。就实战项目而言最有意思的类是DownloadHandlerAssetBundle。它使用工作线程将下载的数据流存储到一个固定大小的缓冲区中然后根据下载处理程序的配置方式将缓冲数据放到临时存储或AssetBundle缓存中。所有这些操作都发生在非托管代码中消除了增加堆内存的风险。此外该下载处理程序并不会保留所有下载字节的栈内存副本从而进一步减少了下载AssetBundle的内存开销。LZMA压缩的AssetBundles将在下载和缓存的时候更改为LZ4压缩。这个可以通过设置Caching.CompressionEnable属性来更改。如果将缓存信息提供给UnityWebRequest对象的话一旦有请求的AssetBundle已经存在于Unity的缓存中那么AssetBundle将立即可用并且此API的行为将会与AssetBundle.LoadFromFile相同操作。在Unity5.6之前UnityWebRequest系统使用了一个固定的工作线程池和一个内部作业系统来防止过多的并发下载并且线程池的大小是不可配置的。在Unity5.6中这些安全措施已经被删除以便适应更现代化的硬件并允许更快地访问HTTP响应代码和报头。3.2.4 WWW.LoadFromCacheOrDownload这是一个很古老的API了从Unity2017.1开始就只是简单地包装了UnityWebRequest。因此使用Unity2017.1或更高版本的开发者应该直接使用UnityWebRequest来工作。Unity已经放弃了对改接口的维护并可能在未来的某个版本中移除。所以下面说的这些内容只适合于Unity5.6或更老的版本。WWW.LoadFromCacheOrDownload允许从远程服务器和本地存储加载对象。也可以通过文件//URL从本地存储加载文件。如果AssetBundle存在于Unitycache中则此API的行为将与AssetBundle.LoadFromFile完全相同。如果AssetBundle尚未缓存则WWW.LoadFromCacheOrDownload会将从它的源文件读取AssetBundle。如果AssetBundle被压缩过它会使用工作线程进行解压缩并写入缓存中。否则它将通过工作线程直接写入缓存。在缓存AssetBundle之后WWW.LoadFromCacheOrDownload将从缓存的、解压缩的AssetBundle加载头信息。然后和AssetBundle.LoadFromFile加载AssetBundle行为相同。此缓存会在WWW.LoadFromCacheOrDownload和UnityWebRequest之间共享。一个API下载的任何AssetBundle也可以通过另一个API获得。虽然数据将通过固定大小的缓冲区解压缩并写入缓存但WWW对象会在本机内存中保留AssetBundle字节的完整副本。这个额外副本被保留的原因是因为要支持WWW.bytes字节属性。由于在WWW对象中缓存AssetBundle的字节的内存开销所以实际项目开发中AssetBundles应该要保持较少的体积以便减少内存。与UnityWebRequest不同的是每次调用这个API都会产生一个新的工作线程。因此在手机等内存有限的平台上最好限定一次只能下载一个AssetBundle以避免内存激增。而在其他平台也要小心创建过多的线程。如果需要下载5个以上的AssetBundles建议在脚本代码中创建和管理下载队列以确保只有少数几个AssetBundle同时下载。3.2.5 建议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保持兼容。3.3 从AssetBundles中加载Assets到这里我们已经能够获得AssetBundles了那么接下来就是要从AssetBundles里获取Assets。Unity提供了三个不同的API从AssetBundles加载UnityEngine.Objects这些API都绑定到AssetBundle对象上并且这些API具有同步和异步变体LoadAsset (LoadAssetAsync)LoadAllAssets (LoadAllAssetsAsync)LoadAssetWithSubAssets (LoadAssetWithSubAssetsAsync)并且这些API的同步版本总是比异步版本快至少一个帧其实是因为异步版本为了确保异步都至少延迟了1帧异步加载每帧会加载多个对象直到他们的时间切片切出。加载多个独立的UnityEngine.Objects时应使用LoadAllAsset。并且只有在需要加载AssetBundle中的大多数或所有对象时才应该使用它。与其他两个API相比LoadAllAsset比对LoadAsset的多个单独调用略快一些。因此如果要加载的Asset数量很大但如果需要一次性加载不到三分之二的AssetBundle的话则考虑将AssetBundle拆分为多个较小的包再使用LoadAllAsset。加载包含多个嵌入式对象的复合Asset时应使用LoadAssetWithSubAsset例如嵌入动画的FBX模型或嵌入多个精灵的sprite图集。也就是说如果需要加载的对象都来自同一Asset但与许多其他无关对象一起存储在AssetBundle中则使用此API。任何其他情况请使用LoadAsset或LoadAssetAsync。3.3.1 低层级的加载细节Object加载是在主线程上执行但数据从工作线程上的存储中读取。任何不触碰Unity系统中线程敏感部分(脚本、图形)的工作都将在工作线程上转换。例如VBO将从网格创建纹理将被解压等等。从Unity5.3开始Object加载就被并行化了。在工作线程上反序列化、处理和集成多个Object。当一个Object完成加载时它的Awake回调将被调用该对象的其余部分将在下一个帧中对UnityEngine可用。同步AssetBundle.Load方法将暂停主线程直到Object加载完成。但它们也会加载时间切片的Object以便Object集成不会占用太多的毫秒帧时间。应用程序属性设置毫秒数的属性为Application.backgroundLoadingPriority。ThreadPriority.High: 每帧最多50毫秒ThreadPriority.Normal: 每帧最多10毫秒ThreadPriority.BelowNormal: 每帧最多4毫秒ThreadPriority.Low: 每帧最多2毫秒。从Unity5.2开始加载多个对象时候会一直进行直到达到对象加载的帧时间限制为止。假设所有其他因素相等asset 加载API的异步变体将总是比同步版本花费更长的时间因为发出异步调用和对象之间有最小的一帧延迟。3.3.2 AssetBundle 依赖项根据运行时环境的不同使用两个不同的API自动跟踪AssetBundles之间的依赖关系。在UnityEditor中可以通过AssetDatabaseAPI查询AssetBundle依赖项。AssetBundles分配和依赖项可以通过AssetImportAPI访问和更改。在运行时Unity提供了一个可选的API通过基于ScriptableObject的AssetBundleManifest API加载在AssetBundle构建过程中生成的依赖信息。当一个或多个AssetBundle 的UnityEngine.Objects引用了一个或者多个其他AssetBundle 的UnityEngine.Objects那么这个AssetBundle就会依赖于另外的AssetBundle。AssetBundles充当由它包含的每个对象的FileGUID和LocalID标识的源数据。因为一个对象是在其Instance ID第一次被间接引用时加载的而且由于一个对象在加载其AssetBundle时被分配了一个有效的Instance ID所以加载AssetBundles的顺序并不重要。相反在加载对象本身之前重要的是加载包含对象依赖关系的所有AssetBundles。Unity不会尝试在加载父AssetBundle时自动加载任何子AssetBundle。示例假设material A引用texture B。material A被打包到AssetBundle1中texture B被打包到AssetBundle2中在本用例中AssetBundle2必须在material A从AssetBundle1中加载之前先加载。这并不意味着AssetBundle 2必须在AssetBundle 1之前加载或者texture B必须从AssetBundle 2中显式加载。在将material A从AssetBundle 1加载之前加载AssetBundle 2就足够了。简单来说就是AssetBundle之间的加载没有先后但是Asset的加载有。有关AssetBundle依赖项的详细信息请参阅手册页。地址https://docs.unity3d.com/Manual/AssetBundles-Dependencies.html?_ga2.168691873.1408835506.1571651191-1030292064.15645830033.3.3. AssetBundle manifests当使用BuildPiine.BuildAssetBundles API执行AssetBundle构建管线时Unity会序列化一个包含每个AssetBundle依赖项信息的对象。此数据存储在单独的AssetBundle中其中包含AssetBundleManifest类型的单个对象。此Asset将存储在与构建AssetBundles的父目录同名的AssetBundle中。如果一个项目将其AssetBundles构建到位于(Projectroot)/Build/Client/的文件夹中那么包含清单的AssetBundle将被保存为(Projectroot)/build/client/Client.manifest。包含Manifest的AssetBundle可以像任何其他AssetBundle一样加载、缓存和卸载。AssetBundleManifest对象本身提供GetAllAssetBundles API来列出与清单同时构建的所有AssetBundles以及查询特定AssetBundle的依赖项的两个方法AssetBundleManifest.GetAllDependencies返回AssetBundle的所有层次依赖项其中包括AssetBundle的直接子级、其子级的依赖项等。AssetBundleManifest.GetDirectDependations只返回AssetBundle的直接子级请注意这两个API分配的都是字符串数组。因此最好是在性能要求不敏感的时候使用。3.3.4. 建议在多数情况下最好在玩家进入应用程序的性能关键区域(如主游戏关卡或世界)之前加载尽可能多的所需对象。这在移动平台上尤为重要因为在移动平台上访问本地存储的速度很慢并且在运行时加载和卸载对象会触发垃圾回收。