首先附上原文链接:https://unity3d.com/learn/tutorials/topics/best-practices/assetbundle-usage-patterns
AssetBundle使用模式
本文是Unity5中关于Asset、Resources和资源管理系列文章的第五章。
本系列的前一章介绍了AssetBundles的基础知识,其中包括各种加载API的低级行为。本章讨论在实际中使用AssetBundle的各个方面的问题和可能的解决方案。
管理已经加载的Asset
在内存敏感的环境中仔细控制加载的Object的大小和数量非常重要。当对象从活动的场景中移除时,Unity不会自动卸载它们。Asset清理会在特定时间触发,也可以通过手动方式触发。
AssetBundles本身必须被小心管理。本地文件存储支持的一个AssetBundle(包括在Unity中缓存的或是通过AssetBundle.LoadFromFile加载的)有最小的内存开销,很少消耗超过几十KB。但是,如果存在大量的AssetBundles,则此开销仍可能会出现问题。
由于大多数项目允许用户去重新体验内容(比如重新进入关卡),因此知道何时加载或卸载AssetBundle非常重要。如果AssetBundle卸载不当,可能会导致内存中的对象重复。 在某些情况下不当卸载AssetBundles也会导致不可预期的行为,例如导致纹理丢失。要理解为什么会发生这种情况,请参阅Asset,Object和序列化章节的“ Object间引用”部分。
管理Asset和AssetBundles时要理解的最重要的事情是调用AssetBundle.Unload时unadAllLoadedObjects参数是true或false的行为差异。
该API将卸载正在调用的AssetBundle的数据头。这个unloadAllLoadedObjects的参数决定是否还卸载从这个AssetBundle实例化的所有Object。如果设置为true,则源于该AssetBundle的所有Object也将立即被卸载 - 即使它们当前正在活动场景中使用。
例如,假设matreial M是从AssetBundle AB中加载的,并且假设M当前处于活动场景中。(图片见原网页)
如果调用了AssetBundle.Unload(true),那么M将从场景中移除,销毁并卸载。但是,如果调用AssetBundle.Unload(false),则AB的数据头将被卸载,但M仍将保留在场景中并且仍然有效。调用AssetBundle.Unload(false)会中断M和AB之间的链接。如果稍后再次加载AB,则AB中包含的该Object的新副本将被加载到内存中。
如果AB再次被加载,则将重新加载AssetBundle数据头的新副本。但是,M并未从AB的这个新副本中加载。Unity不会在AB的新副本和M之间建立任何关联。
如果使用AssetBundle.LoadAsset()来重新加载M,Unity不会认为旧的M副本是AB数据的一个实例。因此,Unity将加载M的新副本,并且在场景中将有两个相同的M副本。
对于大多数项目来说,这种行为是不希望出现的。大多数项目应该使用AssetBundle.Unload(true)并采用一种方法来确保对象不重复。两种常用方法是:
1.在应用程序生命周期中具有明确定义的点(如关卡切换和屏幕加载)卸载临时的AssetBundle。这是更简单和最常见的选择。
2.维持各个Object的引用数量,仅当它们所有构成的Object都处于未使用时,才卸载AssetBundles。这允许应用程序卸载并重新加载各个Object而不存在同样的内存。
如果应用程序必须使用AssetBundle.Unload(false),那么各个Object只能通过两种方式卸载:
1.在场景和代码中消除对所有不需要对象的所有引用。在这完成后,调用Resources.UnloadUnusedAssets。
2.非附加性的加载场景,这将销毁在当前场景中的所有Object,并自动调用Resources.UnloadUnusedAssets。
如果一个项目有明确定义的点,可以使用户等待Object加载和卸载(例如在游戏模式或关卡之间),则应使用这些点来卸载尽可能多的Obejct并加载新的Object。
最简单的方法是将项目的独立块打包到场景中,然后将这些场景及其所有依赖项构建到AssetBundles中。应用程序可以进入“加载”场景,完全卸载包含旧场景的AssetBundle,然后加载包含新场景的AssetBundle。
虽然这是最简单的流程,但有些项目需要更复杂的AssetBundle管理。由于每个项目都不同,因此没有通用的AssetBundle设计模式。
在决定如何将对象分组进AssetBundles时,如果一些Object必须同时加载或者卸载,那么通常最好先将它们打成一个AssetBundle。例如,一个RPG游戏,个别地图和过场动画可按场景分组为AssetBundle,但一些Object在大多数场景中都需要。可以构建一个AssetBundles来提供肖像,游戏中的用户界面以及不同的角色模型和纹理。这些Object和Asset可以被分组到第二个AssetBundle,它们在开始时被加载并在整个应用程序生命周期之内保持加载状态。
如果Unity必须在AssetBundle卸载后从它的AssetBundle重新加载对象,则可能会出现另一个问题。在这种情况下,重新加载将失败,Object将作为(Missing)Object出现在Unity编辑器的层级结构中。
这主要发生在Unity丢失并重新获取对其图形上下文的控制时,例如当移动应用程序被暂停或用户锁定其PC时。在这种情况下,Unity必须将texture和shader重新上传到GPU。如果这些Asset的源AssetBundle不可用,则应用程序将以洋红色呈现场景中的Object。
4.2 分发
将项目的AssetBundles分发给客户端有两种基本方式:与项目同时安装或在安装后下载。
AssetBundle是否跟随安装包安装或是在安装完成后下载取决于项目将要运行平台的能力和限制。移动项目通常选择安装后下载,以减少初始安装大小并保持在无线下载大小限制以下。控制台和PC项目通常会在应用程序初始安装时安装AssetBundle。
正确的体系结构允许在安装后将新内容或修订的内容添加到补丁中,而不管最初如何交付AssetBundles。有关这方面的更多信息,请参阅Unity手册的“ 使用AssetBundles进行修补”部分。
4.2.1 随项目一起安装
将AssetBundles与项目一起安装是最简单的发布方式,因为它不需要额外的下载管理代码。为什么一个项目可能会在安装时包含AssetBundles有两个主要原因:
·减少项目构建时间并允许更简单的迭代开发。如果这些AssetBundles不需要与应用程序分开更新,那么AssetBundle可以通过将AssetBundle存储在Streaming Assets文件夹中而包含在应用程序中。请参阅下面的Streaming Assets部分。
·发布可更新内容的初始版本。通常这样做是为了节省最终用户在初次安装后的时间,或者作为后续打补丁的基础。Streaming Assets对于这种情况并不适合。但是,如果编写自定义下载和缓存系统不是一种选择,则可以从Streaming Assets文件夹中将可更新内容的初始版本加载到Unity缓存中。
4.2.1.1 Streaming Assets
在一个Unity应用程序安装时包含所有类型的内容(包括AssetBundle)的最简单的方式是在构建项目之前将内容放入 /Assets/StreamingAssets/文件夹。构建时包含在StreamingAssets文件夹中的任何内容都将被复制到最终的应用程序中。
本地存储上StreamingAssets文件夹的完整路径可在运行时通过Application.streamingAssetsPath属性访问。然后可以在大多数平台上通过AssetBundle.LoadFromFile加载AssetBundles。
Android开发人员:在Android上,StreamingAssets文件夹中的资源存储在APK中,并且如果它们被压缩可能会需要更多时间才能加载,因为存储在APK中的文件可能使用不同的存储算法,使用的算法可能会因Unity版本而异。您可以使用7-zip等存档器打开APK以确定文件是否被压缩。如果它们确实被压缩了,您可以预料到AssetBundle.LoadFromFile()执行得会更慢。如果是这样,您可以使用UnityWebRequest.GetAssetBundle检索缓存版本作为解决方法。通过使用UnityWebRequest,AssetBundle将在第一次运行期间解压缩并缓存,从而使后续执行速度更快。请注意,这将需要更多的存储空间,因为AssetBundle将被复制到缓存中。或者,您可以导出您的Gradle项目,并在构建时向您的AssetBundles添加扩展。然后,您可以编辑build.gradle文件并将该扩展添加到无压缩部分。完成后,您应该可以使用AssetBundle.LoadFromFile()而无需支付解压缩性能成本。
注意:StreamingAssets在某些平台上不是可写位置。如果项目在安装后需要更新AssetBundle,则可以使用WWW.LoadFromCacheOrDownload或编写自定义下载程序。
4.2.2 安装后下载
将AssetBundles交付给移动设备的最佳方法是在应用程序安装后下载它们。这也允许在安装后更新内容而不强制用户重新下载整个应用程序。在许多平台上,应用程序二进制文件必须经过昂贵且冗长的重新认证过程。因此,开发一个良好的安装后下载系统至关重要。
交付AssetBundles的最简单方法是将它们放置在Web服务器上并通过UnityWebRequest交付。Unity会自动将下载的AssetBundles缓存在本地存储上。如果下载的AssetBundle是LZMA压缩的,则AssetBundle将以未压缩或重新压缩为LZ4(取决于Caching.compressionEnabled设置)存储在缓存中,以便将来加载更快。如果下载的AssetBundle是LZ4压缩的,那么AssetBundle将被压缩存储。如果缓存填满,Unity将从缓存中删除最近最少使用的AssetBundle。有关更多详细信息,请参阅内置缓存部分。
如果可能通常建议使用UnityWebRequest来启动,如果使用Unity 5.2或更老版本只使用WWW.LoadFromCacheOrDownload,如果内置API的内存消耗,缓存行为或性能对于特定项目不可接受,或者项目必须运行特定于平台的代码以实现其要求,则只寄希望于自定义下载系统。
可能影响使用UnityWebRequest或WWW.LoadFromCacheOrDownload的情况示例:
·当需要对AssetBundle缓存进行细粒度控制时
·当项目需要实施自定义压缩策略时
·当项目希望使用平台特定的API来满足某些要求时,例如需要在非活动状态下传输数据。
——比如:使用iOS的后台任务API在后台下载数据。
·当AssetBundles必须在Unity没有适当的SSL支持的平台(如PC)上通过SSL提供。
4.2.3 内置缓存
Unity有一个内置的AssetBundle缓存系统,可用于缓存通过UnityWebRequest API下载的AssetBundle,该API包含一个接受AssetBundle版本号作为参数的重载。此编号不存储在AssetBundle内部,并且不由 AssetBundle系统生成。
缓存系统对传递给UnityWebRequest的最新版本号保持追踪。当此API伴随着一个版本号调用时,缓存系统通过比对版本号来检查是否已经缓存了这个AssetBundle。如果此编号匹配,系统将会加载缓存的AssetBundle。如果此编号不匹配,或是没有一个缓存好的AssetBundle,那么Unity将下载一个新副本。这个新副本将与新版本号相关联。
缓存系统中的AssetBundles仅由其文件名来辨认,而不是由其被下载的完整URL标识。这意味着具有相同文件名的AssetBundle可以存储在多个不同的位置,比如一个CDN(内容分发网络)。只要文件名称相同,缓存系统就会将它们识别为具有相同的AssetBundle。
每个应用程序都要确定一个将版本号分配给AssetBundles的适当策略,并将这些数字传递给UnityWebRequest。这些编号可以是各种独立的标识符种类,例如CRC值。请注意,尽管AssetBundleManifest.GetAssetBundleHash()也可用于此目的,但我们不建议使用此功能进行版本控制,因为它仅提供估算值,而不是真正的哈希值计算。
有关更多详细信息,请参阅Unity手册的“ 使用AssetBundles进行补丁”部分。
在Unity 2017.1以后,缓存API已经扩展提供更多的粒度控制,允许开发人员从多个缓存中选择一个活动缓存。以前的Unity版本只能修改Caching.expirationDelay和Caching.maximumAvailableDiskSpace以删除缓存的项目(这些属性保留在Cache类中的Unity 2017.1中)。
expirationDelay是一个AssetBundle自动删除前必须经过的最小秒数。如果在这期间AssetBundle没有被访问,那么它将会被自动删除。
maximumAvailableDiskSpace指定本地空间存储量(以字节为单位),它是缓存在达到expirationDelay时间开始删除AssetBundle前可以使用的存储空间。达到限制时,Unity将删除最近最少打开的缓存中的AssetBundle(或通过Caching.MarkAsUsed标记为已使用)。Unity会删除缓存的AssetBundles,直到有足够的空间完成新的下载为止。
4.2.3.1 缓存填充
由于AssetBundles由其文件名标识,因此可以使用应用程序附带的AssetBundles“填充”缓存。为此,请将每个AssetBundle的初始版本或基本版本存储在/ Assets / StreamingAssets /中。该过程与“随项目安装”部分中详细介绍的过程相同。
第一次运行应用程序时,可以通过从Application.streamingAssetsPath加载AssetBundles来填充缓存。从此以后,应用程序可以正常调用UnityWebRequest(UnityWebRequest也可以用于最初从StreamingAssets路径加载AssetBundles)。
4.2.3 自定义下载程序
编写自定义下载程序可让应用程序完全控制AssetBundles的下载,解压缩和存储方式。由于所涉及的工程工作意义重大,我们只为大型团队推荐此方法。编写自定义下载器时有四个主要考虑事项:
·下载机制
·存储位置
·压缩类型
·补丁
有关AssetBundle补丁的信息,请参阅Unity手册中使用AssetBundle补丁部分。
4.2.3.1 下载
对于大多数的应用程序来说,HTTP是下载AssetBundles最简单的方法。但是,实现一个基于HTTP的下载程序不是最简单的任务。自定义下载程序必须避免过多的内存分配,过多的线程使用和过多的线程唤醒。在这里详细描述了Unity的WWW类不适合的详细原因。
在编写自定义下载器时,有三个选择:
·C#的HttpWebRequest和WebClient类
·自定义本机插件
·Asset store包
4.2.3.1.1 C#类
如果应用程序不需要HTTPS / SSL支持,则C#的WebClient类提供了下载AssetBundles最简单的机制。它能够将任何文件直接异步下载到本地存储,而无需过多管理内存分配。
要使用WebClient下载AssetBundle,请分配该类的一个实例,并传递用来下载AssetBundle的URL和一个目标路径。如果需要对请求参数进行更多控制,可以使用C#的HttpWebRequest类编写下载程序:
1.从HttpWebResponse.GetResponseStream获取一个字节流。
2.在栈上分配一个固定大小的字节缓冲区。
3.将响应流读入缓冲区。
4.使用C#的File.IO API或任何其他流式IO系统将缓冲写入磁盘。
4.2.3.1.2 Asset Store包
多个Asset Store包提供通过HTTP,HTTPS和其他协议下载文件的本地代码实现。在为Unity编写自定义本机代码插件之前,建议您先评估可用的Asset Store包。
4.2.3.1.3 自定义原生插件
编写自定义本机插件是在Unity中下载数据最耗时,但最灵活的方法。由于编程时间要求高且技术风险高,只有在没有其他方法能够满足应用程序要求的情况下才推荐此方法。例如,如果应用程序必须在Unity中没有C#SSL支持的平台上使用SSL通信,则可能需要自定义本机插件。
自定义本地插件通常会包装目标平台的原生下载API。比如包括iOS上的NSURLConnection和Android上的java.net.HttpURLConnection。请查阅每个平台的原生文档以获取有关使用这些API的更多详细信息。
4.2.3.2 存储
在所有平台上,Application.persistentDataPath指向一个可写的位置,应该用于存储应该在一个应用程序运行之间保持的数据。在编写自定义下载器时,强烈建议使用Application.persistentDataPath的子目录来存储下载的数据。
Application.streamingAssetPath不可写,对于AssetBundle缓存是一个糟糕的选择。
streamingAssetsPath的示例位置包括:
·OSX:在.app包内; 不可写。
·Windows:在安装目录中(例如Program Files); 通常不可写
·iOS:在.ipa包内; 不可写
·Android:在.apk文件中; 不可写
4.3 Asset分配策略
决定怎样划分一个项目的Asset到AssetBundle中并不简单。很容易采用简单的策略,比如将所有Object放置在自己的AssetBundle中或仅使用一个AssetBundle,但这些解决方案具有明显的缺点:
·拥有太少的AssetBundle:
——增加运行时内存占用量
——增加加载时间
——需要更大的下载量
·拥有太多的AssetBundle
——增加项目构建时间
——可能使开发复杂化
——增加总下载时间
关键是决定如何将Object分组为AssetBundle。主要策略是:
·逻辑实体
·Object类型
·同时存在的内容
有关这些分组策略的更多信息可以在手册(https://docs.unity3d.com/Manual/AssetBundles-Preparing.html?_ga=2.23990394.1738950253.1523170803-288792143.1494236420)中找到。
4.4 常见的陷阱
这个部分介绍项目中使用AssetBundles常见的几个问题。
4.5.1 资产重复
Unity5的AssetBundle系统将在Object构建到AssetBundle中时发现Object的所有依赖关系。这个依赖关系信息用于确定包含在AssetBundle中的一组对象。明确分配给一个AssetBundle的Object只会构建到该AssetBundle中。当Object的AssetImporter的assetBundleName属性设置为非空字符串时,对象将被“明确的分配” 。这可以在Unity Editor中通过在Object的监视面板中选择一个AssetBundle或从编辑器脚本中完成。
也可以通过将Object定义为一个AssetBundleBuild映射的一部分来指定其AssetBundle,该映射与BuildPipeline.BuildAssetBundles()方法(接收处理一组AssetBundleBuild)的重载一起使用。
未明确分配AssetBundle的所有Object将会包含在包含1个或多个引用了未标记Object的Object的AssetBundle中。
例如,如果将两个不同的Object分配给两个不同的AssetBundles,但都引用到一个公共依赖的Object,则该依赖Object将被复制到两个 AssetBundles中。重复的依赖关系也将被实例化,这意味着依赖Object的两个副本将被视为具有不同标识符的不同OBject。这将增加应用程序AssetBundles的总大小。如果应用程序加载它的父项,这也会导致Object的两个不同副本被加载到内存中。
有几种方法可以解决这个问题:
1.确保构建到不同AssetBundles中的Object不共享依赖关系。任何共享依赖关系的对象都可以放置在同一个AssetBundle中,而不需要重复依赖关系。它产生的单个AssetBundles必须经常重建和重新下载而造成不方便或低效。
2.分割AssetBundle,以便不会同时加载共享依赖关系的两个AssetBundle。
·此方法可能适用于某些类型的项目,例如基于级别的游戏。但是,它仍然会不必要地增加项目的AssetBundles的大小,并增加构建时间和加载时间。
3.确保所有的依赖关系Asset被全部构建进自己的AssetBundle。这完全消除了重复资产的风险,但也带来了复杂性。应用程序必须跟踪AssetBundles之间的依赖关系,并确保在调用任何AssetBundle.LoadAsset相关API之前加载了正确的AssetBundle。
通过位于UnityEditor名称空间下的AssetDatabase API可以跟踪Object的依赖关系。正如命名空间所暗示的,这个API仅在Unity编辑器中可用,在运行时不可用。
AssetDatabase.GetDependencies可用于查找特定Object或Asset的所有直接依赖关系。请注意,这些依赖关系可能有其自己的依赖关系。此外,AssetImporter API可用于查询任何特定Object被分配的AssetBundle。
通过组合AssetDatabase和AssetImporter API,可以编写一个编辑器脚本,以确保所有AssetBundle的直接或间接依赖关系有被分配的AssetBundle,或者没有两个AssetBundle共享没有被分配给一个AssetBundle的依赖关系。由于重复Asset的内存成本,建议所有项目都有这样的脚本。
4.5.2 Sprite图集重复
任何自动生成的Sprite图集将会被分配到包含生成Sprite图集的Sprite对象的AssetBundle中。如果Sprite对象被分配给多个AssetBundle,那么Sprite图集将不会被分配给一个AssetBundle而是将被复制。如果Sprite对象未分配给AssetBundle,则Sprite图集也不会被分配给AssetBundle。为了确保Sprite图集没有重复,请检查标记在同一个Sprite图集的所有Sprite被分配到同样的AssetBundle中。
请注意,在Unity 5.2.2p3及更早版本中,自动生成的Sprite图集将永远不会被分配给一个AssetBundle。因此,它们将包含在所有包含组成它们的Sprite的AssetBundle中,和任何引用到组成它们Sprite的AssetBundle中。由于这个问题,强烈建议所有使用Unity的sprite packer的Unity 5项目升级到Unity 5.2.2p4,5.3或任何更新版本的Unity。
4.5.3 Android纹理
由于Android生态系统中设备碎片较多,通常需要将纹理压缩为多种不同的格式。虽然所有Android设备都支持ETC1,但ETC1不支持带alpha通道的纹理。如果应用程序不需要OpenGL ES 2支持,则解决该问题的最干净利落方法是使用所有Android OpenGL ES 3设备支持的ETC2。
大多数应用程序需要在ETC2不支持的旧设备上安装。解决此问题的一种方法是使用Unity 5的AssetBundle变体(有关其他选项的详细信息,请参阅Unity的Android优化指南)。
要使用AssetBundle变体,所有不能被ETC1干净利落压缩的纹理都要被分离到只有纹理的AssetBundle。接下来,使用特定供应商的纹理压缩格式(如DXT5,PVRTC和ATITC),来创建关于这些AssetBundle足够的变体来支持安卓生态系统的不能支持ETC2的切片。对于每个AssetBundle变体,更改所包含纹理的TextureImporter设置来选择一个适合变体的压缩环境。
在运行时,可以使用SystemInfo.SupportsTextureFormat API 检测对不同纹理压缩格式的支持。应该使用此信息来选择和加载支持格式的压缩的纹理的AssetBundle变体。
有关Android纹理压缩格式的更多信息可以在这里找到。(链接见原网页)
4.5.4 iOS文件句柄过度使用
Unity的当前版本不受此问题影响。
在Unity 5.3.2p2之前的版本中,Unity会对已经加载的AssetBundle始终持有一个打开的文件句柄。这在大多数平台上都不是问题。但是,iOS平台将进程可以同时打开的文件句柄数限制为255。如果加载AssetBundle导致超出数量限制,则这次加载调用将会失败,并显示“Too Many Open File Handles”错误。
对于尝试将内容分成数百或数千个AssetBundles的项目,这是一个常见问题。
对于无法升级到补丁版本的Unity的项目,临时解决方案是:
·通过合并相关的AssetBundles来减少使用的AssetBundles的数量
·使用AssetBundle.Unload(false)关闭AssetBundle的文件句柄,并手动管理加载的Object的生命周期
4.5 AssetBundle变体
AssetBundle系统的一个关键特性是引入了AssetBundle变体。变体的目的是允许应用程序调整其内容以更好地适配其运行时环境。当加载Object和解析Instance ID引用时,变体允许不同的AssetBundle文件中的UnityEngine.Object显示为“相同”对象。从概念上讲,它允许两个UnityEngine.Object显示为共享相同的File GUID和Local ID,并且通过一个字符串的变体ID来识别实际要加载的UnityEngine.Object。
这个系统有两个主要用例:
1.变体简化了对于指定平台的AssetBundle的加载适配。
·示例:构建系统可以为一个独立的Windows DirectX11应用程序创建一个包含高分辨率纹理和复杂shader的AssetBundle的适配,同时为安卓创建第二个有低保真度内容的AssetBundle。在运行时,项目的资源加载代码可以为其平台加载相应的AssetBundle变体,并且传递到AssetBundle.Load API的Object名称不需要更改。
2.变体允许应用程序在同一平台上加载不同的内容,但应用不同的硬件。
·这是支持各种移动设备的关键。在任何现实世界的应用程序中,iPhone 4都不能像最新的iPhone一样显示相同保真度的内容。
·在Android上,AssetBundle变体可用于解决设备间屏幕纵横比和DPI之间巨大的分裂问题。
4.5.1 限制
AssetBundle变体系统的一个关键限制是它需要使用不同的Asset来构建变体。即使这些Asset之间的唯一差异是其导入设置也适用此限制。如果构建到变体A和变体B中的纹理之间的唯一区别是在Unity纹理导入器中选择的特定纹理压缩算法,则变体A和变体B必须仍然是完全不同的资产。这意味着变体A和变体B必须是磁盘上的单独文件。
这种限制使大型项目的管理复杂化,因为特定Asset的多个副本必须在源代码中保持控制。当开发人员希望更改Asset的内容时,必须更新Asset的所有副本。这个问题没有内置的解决方法。
大多数团队都实施他们自己的AssetBundle变体形式。这是通过在构建AssetBundle时在文件名后添加良好定义的后缀来完成的,以便识别给定AssetBundle所代表的特定变体。在构建这些AssetBundle时,自定义代码以编程方式更改包含的Asset的导入器设置。一些开发者已经扩展了他们的自定义系统,以便能够改变挂在prefab的组件上的参数。
4.6 压缩还是不压缩?
是否压缩AssetBundles需要一些重要的考虑因素,其中包括:
·加载时间:从本地存储或本地缓存加载时,加载未压缩的AssetBundles比加载压缩的AssetBundles要快得多。
·构建时间:在压缩文件时LZMA和LZ4非常缓慢,并且Unity Editor按顺序处理AssetBundle。具有大量AssetBundles的项目将花费大量的时间压缩它们。
·如果AssetBundles在应用程序中安装,则压缩它们将减少应用程序的总大小。
·内存使用情况:在Unity 5.3之前,所有Unity的解压缩机制都要求在解压缩之前将整个压缩的AssetBundle加载到内存中。如果内存使用率很重要,请使用未压缩或LZ4压缩的AssetBundles。
·下载时间:如果AssetBundles很大,或者用户处于带宽受限的环境中,例如在低速或计量连接上下载,那么压缩可能是必须的选择。如果只有几十MB的数据通过高速连接传送到PC,则可以略压缩。
4.6.1 Crunch压缩
主要由使用Crunch压缩算法的DXT压缩纹理组成的包应该被构建为未压缩的。
4.7 AssetBundles和WebGL
Unity强烈建议开发人员不要在WebGL项目上使用压缩的AssetBundle。WebGL项目中的所有AssetBundle解压缩和加载必须在主线程上进行。这是因为Unity的WebGL导出选项当前不支持工作线程。AssetBundle的下载被委托给了浏览器使用在Unity的主线程上执行的XMLHttpRequest。这意味着压缩的AssetBundles在WebGL上加载的消耗非常昂贵。
如果您使用的是Unity 5.5或更早版本,请考虑避免在压缩AssetBundle时使用LZMA,而是使用按需解压缩非常效率的LZ4进行压缩作为代替。如果您需要更小的压缩大小并使用LZ4进行传输,您可以配置您的Web服务器在HTTP协议层面(在LZ4压缩之上)对文件进行gzip压缩。Unity 5.6在WebGL平台删除了LZMA作为压缩的选项。