Unity文档——AssetBundle基本原理

AssetBundle基本原理

版本检查:2017.3

-

难度:高级

 

这是关于Unity 5中资产,资源和资源管理系列文章的第四章。

本章讨论AssetBundles。它介绍了构建AssetBundle的基本系统,以及用于与AssetBundles交互的核心API。特别是,它讨论了AssetBundles本身的加载和卸载,以及AssetBundles中特定Asset和Objects的加载和卸载。

有关AssetBundles使用的更多模式和最佳实践,请参阅本系列的下一章。

3.1。概观

AssetBundle系统提供了一种以Unity可以索引和序列化的存档格式存储一个或多个文件的方法。AssetBundles是Unity在安装后交付和更新非代码内容的主要工具。这允许开发人员提交较小的应用程序包,最小化运行时内存压力,并有选择地加载针对最终用户设备优化的内容。

了解AssetBundles的工作方式对于为移动设备构建成功的Unity项目至关重要。有关AssetBundle内容的整体说明,请查看AssetBundle文档

3.2。AssetBundle布局

总而言之,AssetBundle由两部分组成:标题和数据段。

标头包含有关AssetBundle的信息,例如其标识符,压缩类型和清单。清单是一个由Object名称键入的查找表。每个条目都提供一个字节索引,指示在AssetBundle的数据段中可以找到给定Object的位置。在大多数平台上,此查找表实现为平衡搜索树。具体来说,Windows和OSX派生的平台(包括iOS)使用红黑树。因此,随着AssetBundle内资产数量的增加,构建清单所需的时间将增加超过线性

数据段包含通过序列化AssetBundle中的资产生成的原始数据。如果将LZMA指定为压缩方案,则会压缩所有序列化资产的完整字节数组。如果指定了LZ4,则单独压缩单独Assets的字节。如果不使用压缩,则数据段将保留为原始字节流。

在Unity 5.3之前,无法在AssetBundle中单独压缩对象。因此,如果指示5.3之前的Unity版本从压缩的AssetBundle读取一个或多个对象,则Unity必须解压缩整个AssetBundle。通常,Unity会缓存AssetBundle的解压缩副本,以提高同一AssetBundle上后续加载请求的加载性能。

3.3。加载AssetBundles

AssetBundles可以通过四个不同的API加载。这四种API的行为根据两个标准而有所不同:

  1. AssetBundle是否为LZMA压缩,LZ4是压缩还是未压缩
  2. 正在加载AssetBundle的平台

这些API是:

  • AssetBundle.LoadFromMemory(异步可选)

  • AssetBundle.LoadFromFile(异步可选)

  • UnityWebRequest的DownloadHandlerAssetBundle

  • WWW.LoadFromCacheOrDownload(在Unity 5.6或更早版本上)

3.3.1 AssetBundle.LoadFromMemory(Async)

Unity建议不要使用此API。

AssetBundle.LoadFromMemoryAsync从托管代码字节数组(C#中的byte [])加载AssetBundle 。它总是将源数据从托管代码字节数组复制到新分配的,连续的本机内存块中。如果AssetBundle是LZMA压缩的,它将在复制时解压缩AssetBundle。未压缩和LZ4压缩的AssetBundles将逐字复制。

此API消耗的最大内存量至少是AssetBundle大小的两倍:API创建的本机内存中的一个副本,以及传递给API的托管字节数组中的一个副本。因此,通过此API创建的AssetBundle加载的资产将在内存中重复三次:一次在托管代码字节数组中,一次在AssetBundle的本机内存副本中,第三次在GPU或系统内存中用于资产本身。

在Unity 5.3.3之前,此API称为AssetBundle.CreateFromMemory。它的功能没有改变。

3.3.2。AssetBundle.LoadFromFile(异步)

AssetBundle.LoadFromFile是一种高效API,用于从本地存储(如硬盘或SD卡)加载未压缩或LZ4压缩的AssetBundle。

在桌面独立,控制台和移动平台上,API将仅加载AssetBundle的标头,并将剩余数据保留在磁盘上。当调用加载方法(例如AssetBundle.Load)或取消引用其InstanceID时,AssetBundle的对象将按需加载。在这种情况下,不会消耗多余的内存。在Unity Editor中,API会将整个AssetBundle加载到内存中,就像从磁盘读取字节并使用AssetBundle.LoadFromMemoryAsync一样。如果在Unity Editor中对项目进行了分析,则此API可能会导致在AssetBundle加载期间出现内存峰值。这不应该影响设备上的性能,并且应该在采取补救措施之前在设备上重新测试这些尖峰。

注意:在使用Unity 5.3或更早版本的Android设备上,尝试从Streaming Assets路径加载AssetBundles时,此API将失败。Unity 5.4中已解决此问题。有关更多详细信息,请参阅AssetBundle使用模式一章中的分发 - 随项目一起提供的部分。

在Unity 5.3之前,此API称为AssetBundle.CreateFromFile。它的功能没有改变。

3.3.3。AssetBundleDownloadHandler

UnityWebRequest API允许开发人员指定统一究竟应该如何处理下载的数据,并允许开发者以消除不必要的内存使用情况。使用UnityWebRequest下载AssetBundle的最简单方法是调用UnityWebRequest.GetAssetBundle

出于本指南的目的,感兴趣的类是DownloadHandlerAssetBundle。使用工作线程,它将下载的数据流式传输到固定大小的缓冲区,然后将缓冲的数据假脱机到临时存储或AssetBundle缓存,具体取决于下载处理程序的配置方式。所有这些操作都在本机代码中进行,从而消除了扩展托管堆的风险。此外,该下载处理程序并没有把所有下载的字节的本机代码副本,进一步降低了下载的AssetBundle的内存开销。

LZMA压缩的AssetBundles将在下载期间解压缩并使用LZ4压缩进行缓存。可以通过设置Caching.CompressionEnabled来更改此行为。

当下载完成后,assetBundle下载处理程序的属性提供下载的AssetBundle,仿佛AssetBundle.LoadFromFile已经呼吁下载AssetBundle。

如果向UnityWebRequest对象提供缓存信息,并且Unity缓存中已存在所请求的AssetBundle,那么AssetBundle将立即可用,并且此API将与AssetBundle.LoadFromFile完全相同。

在Unity 5.6之前,UnityWebRequest系统使用固定的工作线程池和内部作业系统来防止过多的并发下载。线程池的大小不可配置。在Unity 5.6中,已删除这些安全措施以适应更多现代硬件,并允许更快地访问HTTP响应代码和标头。

3.3.4。WWW.LoadFromCacheOrDownload

*注意:从Unity 2017.1开始,WWW.LoadFromCacheOrDownload简单地包装UnityWebRequest。因此,使用Unity 2017.1或更高版本的开发人员应迁移到UnityWebRequest。WWW.LoadFromCacheOrDownload将在以后的版本中弃用。*

以下信息适用于Unity 5.6或更早版本。

WWW.LoadFromCacheOrDownload是一个允许从远程服务器和本地存储加载对象的API。可以通过file:// URL从本地存储加载文件。如果Unity缓存中存在AssetBundle,则此API的行为与AssetBundle.LoadFromFile完全相同。

如果尚未缓存AssetBundle,则WWW.LoadFromCacheOrDownload将从其源读取AssetBundle。如果AssetBundle被压缩,它将使用工作线程解压缩并写入缓存。否则,它将通过工作线程直接写入缓存。缓存AssetBundle后WWW.LoadFromCacheOrDownload将从缓存的解压缩AssetBundle加载标头信息。然后,API的行为与使用AssetBundle.LoadFromFile加载的AssetBundle完全相同。此缓存在WWW.LoadFromCacheOrDownloadUnityWebRequest之间共享。使用一个API下载的任何AssetBundle也可通过其他API获得。

虽然数据将通过固定大小的缓冲区解压缩并写入缓存,但WWW对象将在本机内存中保留AssetBundle字节的完整副本。保留AssetBundle的额外副本以支持WWW.bytes属性。

由于在WWW对象中缓存AssetBundle字节的内存开销,AssetBundles应保持较小 - 最多几兆字节。有关AssetBundle大小调整的更多讨论,请参阅AssetBundle使用模式一章中的资产分配策略部分。

与UnityWebRequest不同,每次调用此API都会产生一个新的工作线程。因此,在具有有限内存的平台(例如移动设备)上,应该使用此API一次只下载一个AssetBundle,以避免内存峰值。多次调用此API时,请注意创建过多的线程。如果需要下载超过5个AssetBundle,请在脚本代码中创建和管理下载队列,以确保仅同时运行少量AssetBundle下载。

3.3.5。建议

通常,应尽可能使用AssetBundle.LoadFromFile。就速度,磁盘使用和运行时内存使用而言,此API是最有效的。

对于必须下载或补丁AssetBundles项目,强烈建议使用UnityWebRequest使用Unity 5.3或更高版本,以及项目WWW.LoadFromCacheOrDownload使用Unity 5.2或以上的项目。如下一章的“ 分发”部分所述,可以使用项目安装程序中包含的Bundle来填充AssetBundle Cache。

使用UnityWebRequest *或* WWW.LoadFromCacheOrDownload时,请确保在加载AssetBundle后下载程序代码正确调用Dispose。或者,C#的using语句是确保安全地处理WWWUnityWebRequest的最方便的方法。

对于需要具有独特,特定缓存或下载要求的大量工程团队的项目,可以考虑使用自定义下载程序。编写自定义下载程序是一项非常重要的工程任务,任何自定义下载程序都应与AssetBundle.LoadFromFile兼容有关更多详细信息,请参阅下一章的“ 分发”部分。

3.4。从AssetBundles加载资产

UnityEngine.Objects可以使用三个不同的API从AssetBundles加载,这些API都附加到AssetBundle对象,它们具有同步和异步变体:

这些API的同步版本总是比其异步版本快至少一帧。

异步加载将每帧加载多个对象,直到其时间片限制。有关此行为的基本技术原因,请参阅低级加载详细信息部分。

LoadAllAssets应该加载多个独立UnityEngine.Objects时使用。它只应在需要加载AssetBundle中的大多数或所有对象时使用。与其他两个API相比,LoadAllAssets比对LoadAssets的多个单独调用稍快。因此,如果要加载的资产数量很大,但是一次只需要加载少于66%的AssetBundle,请考虑将AssetBundle拆分为多个较小的包并使用LoadAllAssets

加载包含多个嵌入对象的复合资产时,应使用LoadAssetWithSubAssets,例如具有嵌入动画的FBX模型或嵌入了多个精灵的精灵图集。如果需要加载的对象全部来自同一资产,但存储在带有许多其他无关对象的AssetBundle中,则使用此API。

对于任何其他情况,请使用LoadAssetLoadAssetAsync

3.4.1。低级加载详细信息

UnityEngine.Object加载是在主线程上执行的:从工作线程的存储中读取Object的数据。任何不接触Unity系统的线程敏感部分(脚本,图形)的东西都将在工作线程上转换。例如,VBO将从网格创建,纹理将被解压缩等。

从Unity 5.3开始,对象加载已经并行化。多个对象在工作线程上反序列化,处理和集成。当Object完成加载时,将调用其Awake回调,并且Object将在下一帧期间对Unity Engine的其余部分可用。

同步AssetBundle.Load方法将暂停主线程,直到对象加载完成。它们还会对对象加载进行时间分片,以便对象集成不占用超过一定毫秒的帧时间。毫秒数由属性Application.backgroundLoadingPriority设置:

  • ThreadPriority.High:每帧最多50毫秒

  • ThreadPriority.Normal:每帧最多10毫秒

  • ThreadPriority.BelowNormal:每帧最多4毫秒

  • ThreadPriority.Low:每帧最多2毫秒。

从Unity 5.2开始,加载多个对象,直到达到对象加载的帧时间限制。假设所有其他因素相等,资产加载API的异步变体将始终比可比较的同步版本更长,因为发出异步调用和引擎可用的对象之间的最小一帧延迟。

3.4.2。AssetBundle依赖项

AssetBundle之间的依赖关系使用两个不同的API自动跟踪,具体取决于运行时环境。在Unity Editor中,可以通过AssetDatabase API 查询AssetBundle依赖。可以通过AssetImporter API 访问和更改AssetBundle分配和依赖。在运行时,Unity提供了一个可选API,用于通过基于ScriptableObject的AssetBundleManifest API 加载在AssetBundle构建期间生成的依赖关系信息。

当一个或多个父AssetBundle的UnityEngine.Object引用一个或多个其他AssetBundle的UnityEngine.Object时,AssetBundle依赖于另一个AssetBundle。有关对象间引用的更多信息,请参阅“ 资产,对象和序列化”一文的“ 对象间引用”部分。

如该文章的序列化和实例部分所述,AssetBundles充当由AssetBundle中包含的每个Object的FileGUID和LocalID标识的源数据的源。

因为在首次取消引用其实例ID时加载了Object,并且因为在加载AssetBundle时为Object分配了有效的实例ID,所以AssetBundle的加载顺序并不重要。相反,在加载Object本身之前加载包含Object依赖关系的所有AssetBundle非常重要。加载父AssetBundle时,Unity不会尝试自动加载任何子AssetBundle。

例:

假设材料A是指组织B。材料A打包到AssetBundle 1中,纹理B打包到AssetBundle 2中。

描述

在此用例中,必须在从AssetBundle 1加载材料A 之前加载AssetBundle 2。

这并不意味着必须在AssetBundle 1之前加载AssetBundle 2,或者必须从AssetBundle 2明确加载Texture B.在将Asset A加载到AssetBundle 1之前加载AssetBundle 2就足够了。

但是,在加载AssetBundle 1时,Unity 不会自动加载AssetBundle 2。这必须在脚本代码中手动完成。

有关AssetBundle依赖关系的更多信息,请参阅手册页

3.4.3。AssetBundle表现出来

使用BuildPipeline.BuildAssetBundles API 执行AssetBundle构建管道时,Unity会序列化包含每个AssetBundle依赖项信息的Object。此数据存储在单独的AssetBundle中,该AssetBundle包含AssetBundleManifest类型的单个Object 。

此资产将存储在AssetBundle中,其名称与构建AssetBundle的父目录的名称相同。如果项目将其AssetBundle构建到(projectroot)/ build / Client /的文件夹,则包含清单的AssetBundle将保存为(projectroot)/build/Client/Client.manifest

包含清单的AssetBundle可以像任何其他AssetBundle一样加载,缓存和卸载。

AssetBundleManifest对象本身提供了GetAllAssetBundles API,用于列出与清单同时构建的所有AssetBundle,以及两种查询特定AssetBundle依赖关系的方法:

请注意,这两个API都分配字符串数组。因此,它们应该只是谨慎使用,而不是在应用程序生命周期的性能敏感部分使用。

3.4.4。建议

在许多情况下,最好在玩家进入应用程序的性能关键区域(例如主游戏级别或世界)之前加载尽可能多的所需对象。这在移动平台上尤其重要,因为移动平台对本地存储的访问速度很慢,并且在播放时加载和卸载对象的内存流失可以触发垃圾收集器。

对于必须在应用程序交互时加载和卸载对象的项目,请参阅AssetBundle使用模式文章的管理已加载资源部分,以获取有关卸载对象和AssetBundle的更多信息。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
数据来源:中经数据库 主要指标110多个(全部都是纯粹的 市辖区 指标),大致是: GDP GDP增速 第一产业增加值占GDP比重 第二产业增加值占GDP比重 第三产业增加值占GDP比重 人均GDP 社会消费品零售总额 固定资产投资(不含农户) 新设外商投资企业数_外商直接投资 实际利用外资金额(美元) 一般公共预算收入 一般公共预算支出 一般公共预算支出_教育 一般公共预算支出_科学技术 金融机构人民币各项存款余额_个人储蓄存款 金融机构人民币各项存款余额 金融机构人民币各项贷款余额 规模以上工业企业单位数 规模以上工业企业单位数_内资企业 规模以上工业企业单位数_港澳台商投资企业 规模以上工业企业单位数_外商投资企业 规模以上工业总产值 规模以上工业总产值_内资企业 规模以上工业总产值_港澳台商投资企业 规模以上工业总产值_外商投资企业 规模以上工业企业流动资产合计 规模以上工业企业固定资产合计 规模以上工业企业利润总额 规模以上工业企业应交增值税 规模以上工业企业主营业务税金及附加 户籍人口数 年均户籍人口数 户籍人口自然增长率 第一产业就业人员占全部城镇单位就业人员比重 第二产业就业人员占全部城镇单位就业人员比重 第三产业就业人员占全部城镇单位就业人员比重 城镇非私营单位就业人员数 城镇非私营单位就业人员数_第一产业 城镇非私营单位就业人员数_第二产业 城镇非私营单位就业人员数_第三产业 城镇非私营单位就业人员数_农、林、牧、渔业 城镇非私营单位就业人员数_采矿业 城镇非私营单位就业人员数_制造业 城镇非私营单位就业人员数_电力、热力、燃气及水生产和供应业 城镇非私营单位就业人员数_建筑业 城镇非私营单位就业人员数_批发和零售业 城镇非私营单位就业人员数_交通运输、仓储和邮政业 城镇非私营单位就业人员数_住宿和餐饮业 城镇非私营单位就业人员数_信息传输、软件和信息技术服务业 城镇非私营单位就业人员数_金融业 城镇非私营单位就业人员数_房地产业 城镇非私营单位就业人员数_租赁和商务服务业 城镇非私营单位就业人员数_科学研究和技术服务业 城镇非私营单位就业人员数_水利、环境和公共设施管理业 城镇非私营单位就业人员数_居民服务、修理和其他服务业 城镇非私营单位就业人员数_教育 城镇非私营单位就业人员数_卫生和社会工作 城镇非私营单位就业人员数_文化、体育和娱乐业 城镇非私营单位就业人员数_公共管理、社会保障和社会组织 城镇非私营单位在岗职工平均人数 城镇就业人员数_私营企业和个体 城镇非私营单位在岗职工工资总额 城镇非私营单位在岗职工平均工资 城镇登记失业人员数 建成区面积 建设用地面积 建设用地面积_居住用地 液化石油气供气总量 液化石油气供气总量_居民家庭 人工煤气、天然气供气总量 人工煤气、天然气供气总量_居民家庭 液化石油气用气人口 人工煤气、天然气用气人口 城市公共汽电车运营车辆数 城市出租汽车运营车辆数 城市公共汽电车客运总量 道路面积 排水管道长度 建成区绿化覆盖面积 建成区绿化覆盖率 绿地面积 公园绿地面积 维护建设资金支出 土地面积 生活用水供水量 供水总量 全社会用电量 城乡居民生活用电量 工业生产用电量 房地产开发投资 房地产开发投资_住宅 限额以上批发和零售业法人单位数 限额以上批发和零售业商品销售总额 普通中学学校数 中等职业教育学校数 普通小学学校数 普通高等学校专任教师数 普通中学专任教师数 中等职业教育专任教师数 普通小学专任教师数 普通高等学校在校生数 普通中学在校生数 中等职业教育在校生数 普通小学在校生数 电视节目综合人口覆盖率 公共图书馆总藏量_图书 医疗卫生机构数_医院和卫生院 卫生人员数_执业(助理)医师 医疗卫生机构床位数_医院和卫生院 城镇职工基本养老保险参保人数 职工基本医疗保险参保人数 失业保险参保人数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值