赞
踩
从官网复制黏贴的 Unity之AB包
要开始使用 AssetBundle,请按照以下步骤操作。有关每个工作流程的更多详细信息,请参阅本文档这一部分的其他页面。
/
分隔文件夹名称。例如,使用 AssetBundle 名称 environment/forest
在 environment
子文件夹下创建名为 forest
的捆绑包要阅读有关 AssetBundle 分配和相关策略的更多信息,请参阅关于为 AssetBundle 准备资源的文档。
在 Assets 文件夹中创建一个名为 Editor 的文件夹,并将包含以下内容的脚本放在该文件夹中:
using UnityEditor; using System.IO; public class CreateAssetBundles { [MenuItem("Assets/Build AssetBundles")] static void BuildAllAssetBundles() { string assetBundleDirectory = "Assets/AssetBundles"; if(!Directory.Exists(assetBundleDirectory)) { Directory.CreateDirectory(assetBundleDirectory); } BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows); } }
此脚本将在 Assets 菜单底部创建一个名为 Build AssetBundles 的菜单项,该菜单项将执行与该标签关联的函数中的代码。单击 Build AssetBundles 时,将随构建对话框一起显示一个进度条。此过程将会获取带有 AssetBundle 名称标签的所有资源,并将它们放在 assetBundleDirectory
定义的路径中的文件夹中。
如需了解与此相关的更多详细信息,请参阅关于构建 AssetBundle 的文档。
如果您想从本地存储中加载,请使用 AssetBundles.LoadFromFile
API,如下所示:
public class LoadFromFileExample : MonoBehaviour {
function Start() {
var myLoadedAssetBundle
= AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
if (myLoadedAssetBundle == null) {
Debug.Log("Failed to load AssetBundle!");
return;
}
var prefab = myLoadedAssetBundle.LoadAsset<GameObject>("MyObject");
Instantiate(prefab);
}
}
LoadFromFile
获取捆绑包文件的路径。
如果是您自己托管 AssetBundle 并需要将它们下载到应用程序中,请使用 UnityWebRequest
API。下面是一个示例:
IEnumerator InstantiateObject()
{
string url = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request
= UnityEngine.Networking.UnityWebRequest.GetAssetBundle(url, 0);
yield return request.Send();
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");
Instantiate(cube);
Instantiate(sprite);
}
GetAssetBundle(string, int)
获取 AssetBundle 的位置 URL 以及要下载的捆绑包的版本。此示例仍然指向一个本地文件,但 string url
可以指向托管 AssetBundle 的任何 URL。
UnityWebRequest 有一个特定的句柄来处理 AssetBundle:DownloadHandlerAssetBundle
,可根据请求获取 AssetBundle。
无论使用哪种方法,现在都可以访问 AssetBundle 对象了。对该对象需要使用 LoadAsset<T>(string)
,此函数将获取您尝试加载的资源的类型 T
以及对象的名称(作为捆绑包内部的字符串)。这将返回从 AssetBundle 加载的任何对象。可以像使用 Unity 中的任何对象一样使用这些返回的对象。例如,如果要在场景中创建游戏对象,只需调用 Instantiate(gameObjectFromAssetBundle)
。
有关用于加载 AssetBundle 的 API 的更多信息,请参阅关于本机使用 AssetBundle 的文档。
使用 AssetBundle 时,可以任意将任何资源分配给所需的任何捆绑包。但是,在设置捆绑包时需要考虑某些策略。以下分组策略旨在用于您认为适合的具体项目。可以根据需要随意混合和搭配这些策略。
逻辑实体分组是指根据资源所代表的项目功能部分将资源分配给 AssetBundle。这包括各种不同部分,比如用户界面、角色、环境以及在应用程序整个生命周期中可能经常出现的任何其他内容。
逻辑实体分组非常适合于可下载内容 (DLC),因为通过这种方式将所有内容隔离后,可以对单个实体进行更改,而无需下载其他未更改的资源。
为了能够正确实施此策略,最大诀窍在于,负责为各自捆绑包分配资源的开发人员必须熟悉项目使用每个资源的准确时机和场合。
根据此策略,可以将相似类型的资源(例如音频轨道或语言本地化文件)分配到单个 AssetBundle。
要构建供多个平台使用的 AssetBundle,类型分组是最佳策略之一。例如,如果音频压缩设置在 Windows 和 Mac 平台上完全相同,则可以自行将所有音频数据打包到 AssetBundle 并重复使用这些捆绑包,而着色器往往使用更多特定于平台的选项进行编译,因此为 Mac 构建的着色器捆绑包可能无法在 Windows 上重复使用。此外,这种方法非常适合让 AssetBundle 与更多 Unity 播放器版本兼容,因为纹理压缩格式和设置的更改频率低于代码脚本或预制件。
并发内容分组是指将需要同时加载和使用的资源捆绑在一起。可以将这些类型的捆绑包用于基于关卡的游戏(其中每个关卡包含完全独特的角色、纹理、音乐等)。有时可能希望确保其中一个 AssetBundle 中的资源与该捆绑包中的其余资源同时使用。依赖于并发内容分组捆绑包中的单个资源会导致加载时间显著增加。您将被迫下载该单个资源的整个捆绑包。
并发内容分组捆绑包最常见的用例是针对基于场景的捆绑包。在此分配策略中,每个场景捆绑包应包含大部分或全部场景依赖项。
请注意,项目绝对可以也应该根据您的需求混用这些策略。对任何给定情形使用最优资源分配策略可以大大提高项目的效率。
例如,一个项目可能决定将不同平台的用户界面 (UI) 元素分组到各自的 Platform-UI 特定捆绑包中,但按关卡/场景对其交互式内容进行分组。
在有关 AssetBundle 工作流程的文档中,我们有一个代码示例将三个参数传递给 BuildPipeline.BuildAssetBundles
函数。让我们深入了解一下这方面。
Assets/AssetBundles:这是 AssetBundle 要输出到的目录。可以将其更改为所需的任何输出目录,只需确保在尝试构建之前文件夹实际存在。
可以指定几个具有各种效果的不同 BuildAssetBundleOptions
。请参阅关于 BuildAssetBundleOptions 的脚本 API 参考查看所有这些选项的表格。
虽然可以根据需求变化和需求出现而自由组合 BuildAssetBundleOptions
,但有三个特定的 BuildAssetBundleOptions
可以处理 AssetBundle 压缩:
BuildAssetBundleOptions.None
:此捆绑包选项使用 LZMA 格式压缩,这是一个压缩的 LZMA 序列化数据文件流。LZMA 压缩要求在使用捆绑包之前对整个捆绑包进行解压缩。此压缩使文件大小尽可能小,但由于需要解压缩,加载时间略长。值得注意的是,在使用此 BuildAssetBundleOptions 时,为了使用捆绑包中的任何资源,必须首先解压缩整个捆绑包。BuildAssetBundleOptions.UncompressedAssetBundle
: This bundle option builds the bundles in such a way that the data is completely uncompressed. The downside to being uncompressed is the larger file download size. However, the load times once downloaded will be much faster. Uncompressed AssetBundles are 16-byte aligned.BuildAssetBundleOptions.ChunkBasedCompression
:此捆绑包选项使用称为 LZ4 的压缩方法,因此压缩文件大小比 LZMA 更大,但不像 LZMA 那样需要解压缩整个包才能使用捆绑包。LZ4 使用基于块的算法,允许按段或“块”加载 AssetBundle。解压缩单个块即可使用包含的资源,即使 AssetBundle 的其他块未解压缩也不影响。使用 ChunkBasedCompression
时的加载时间与未压缩捆绑包大致相当,额外的优势是减小了占用的磁盘大小。
BuildTarget.Standalone
:这里我们告诉构建管线,我们要将这些 AssetBundle 用于哪些目标平台。可以在关于 BuildTarget 的脚本 API 参考中找到可用显式构建目标的列表。但是,如果不想在构建目标中进行硬编码,请充分利用 EditorUserBuildSettings.activeBuildTarget
,它将自动找到当前设置的目标构建平台,并根据该目标构建 AssetBundle。
一旦正确设置构建脚本,最后便可以开始构建资源包了。如果是按照上面的脚本示例进行的操作,请单击 Assets > Build AssetBundles 以开始该过程。
现在已经成功构建了 AssetBundle,您可能会注意到 AssetBundles 目录包含的文件数量超出了最初的预期。确切地说,是多出了 2*(n+1) 个文件。让我们花点时间详细了解一下 BuildPipeline.BuildAssetBundles
产生的结果。
对于在编辑器中指定的每个 AssetBundle,可以看到一个具有 AssetBundle 名称+“.manifest”的文件。
随后会有一个额外捆绑包和清单的名称不同于先前创建的任何 AssetBundle。相反,此包以其所在的目录(构建 AssetBundle 的目录)命名。这就是清单捆绑包。我们以后会对此进行详细讨论并介绍使用方法。
这是缺少 .manifest 扩展名的文件,其中包含在运行时为了加载资源而需要加载的内容。
AssetBundle 文件是一个存档,在内部包含多个文件。此存档的结构根据它是 AssetBundle 还是场景 AssetBundle 可能会略有不同。以下是普通 AssetBundle 的结构:
场景 AssetBundle 与普通 AssetBundle 的不同之处在于,它针对场景及其内容的串流加载进行了优化。
对于生成的每个捆绑包(包括附加的清单捆绑包),都会生成关联的清单文件。清单文件可以使用任何文本编辑器打开,并包含诸如循环冗余校验 (CRC) 数据和捆绑包的依赖性数据之类的信息。对于普通 AssetBundle,它们的清单文件将如下所示:
ManifestFileVersion: 0 CRC: 2422268106 Hashes: AssetFileHash: serializedVersion: 2 Hash: 8b6db55a2344f068cf8a9be0a662ba15 TypeTreeHash: serializedVersion: 2 Hash: 37ad974993dbaa77485dd2a0c38f347a HashAppended: 0 ClassTypes: - Class: 91 Script: {instanceID: 0} Assets: Asset_0: Assets/Mecanim/StateMachine.controller Dependencies: {}
其中显示了包含的资源、依赖项和其他信息。
生成的清单捆绑包将有一个清单,但看起来更可能如下所示:
ManifestFileVersion: 0
AssetBundleManifest:
AssetBundleInfos:
Info_0:
Name: scene1assetbundle
Dependencies: {}
这将显示 AssetBundle 之间的关系以及它们的依赖项。就目前而言,只需要了解这个捆绑包中包含 AssetBundleManifest 对象,这对于确定在运行时加载哪些捆绑包依赖项非常有用。要了解有关如何使用此捆绑包和清单对象的更多信息,请参阅有关本机使用 AssetBundle 的文档。
如果一个或多个 UnityEngine.Objects
包含对位于另一个捆绑包中的 UnityEngine.Object
的引用,则 AssetBundle 可以变为依赖于其他 AssetBundle。如果 UnityEngine.Object
包含对任何 AssetBundle 中未包含的 UnityEngine.Object
的引用,则不会发生依赖关系。在这种情况下,在构建 AssetBundle 时,捆绑包所依赖的对象的副本将复制到捆绑包中。如果多个捆绑包中的多个对象包含对未分配给捆绑包的同一对象的引用,则每个对该对象具有依赖关系的捆绑包将创建其自己的对象副本并将其打包到构建的 AssetBundle 中。
如果 AssetBundle 中包含依赖项,则在加载尝试实例化的对象之前,务必加载包含这些依赖项的捆绑包。Unity 不会尝试自动加载依赖项。
参考以下示例,Bundle 1 中的材质引用了 Bundle 2 中的纹理:
在此示例中,在从 Bundle 1 加载材质之前,需要将 Bundle 2 加载到内存中。加载 Bundle 1 和 Bundle 2 的顺序无关紧要,重要的是在从 Bundle 1 加载材质之前应加载 Bundle 2。在下一部分,我们将讨论如何使用我们在上一部分介绍的 AssetBundleManifest
对象在运行时确定并加载依赖项。
默认情况下,Unity 不会优化 AssetBundle 之间的重复信息。这意味着您项目中的多个 AssetBundle 可能包含相同信息,例如多个预制件使用的同一种材质。在多个 AssetBundle 中使用的资源被称为公共资源。这些会影响内存资源和加载时间。
本页介绍 Unity 如何管理 AssetBundle 之间的重复信息,以及您可以如何应用优化措施。
默认情况下,Unity 不会执行任何优化措施来减少或最小化存储重复信息所需的内存。在创建构建版本期间,Unity 会在 AssetBundle 中对隐式引用的资源构建重复版本。 为避免发生此类重复,请将公共资源(例如材质)分配到它们自身的 AssetBundle。
例如:可能有一个应用程序包含两个预制件,这两个预制件都分配到它们自身的 AssetBundle。两个预制件共享相同材质,而该材质未分配到 AssetBundle。该材质引用一个纹理,而该纹理也未分配到 AssetBundle。
如果您遵循 AssetBundle 工作流程并使用示例类 CreateAssetBundles
来构建 AssetBundle,每个生成的 AssetBundle 都会包含此材质(包括其着色器和引用的纹理)。在以下示例图像中,预制件文件的大小分别为 383 KB 和 377 KB。
如果项目包含更多数量的预制件,此行为会影响最终安装程序的大小以及加载这两个 AssetBundle 时的运行时内存占用量。由于 Unity 将同一材质的每个副本视为独特材质,因此 AssetBundle 之间的数据重复问题也会影响批处理。
为了避免发生数据重复,请将材质及其引用的资源分配给其各自的 modulesmaterials
AssetBundle。还可以仅标记材质,因为构建过程中,纹理依赖关系也会被自动包含在 AssetBundle 中。
现在,如果重新构建 AssetBundle,则生成的输出包含单独的 modulesmaterials
AssetBundle (359 KB),其中包含此材质及其关联的纹理。这会显著减小预制件的其他 AssetBundle 的大小(从上一个迭代的大约 380 KB 减小到大约 20 KB)。
下图说明了此情况。
将公共资源提取到单个 AssetBundle 时,请注意依赖关系。特别要注意的是,如果仅使用预制件来实例化模块,则不会加载材质。
一个未加载材质的预制件
要解决此问题,请先在内存中加载材质 AssetBundle,然后再加载属于模块的 AssetBundle。在以下示例中,这个过程发生在变量 materialsAB
中。
using System.IO; using UnityEngine; public class InstantiateAssetBundles : MonoBehaviour { void Start() { var materialsAB = AssetBundle.LoadFromFile(Path.Combine(Application.dataPath, Path.Combine("AssetBundles", "modulesmaterials"))); var moduleAB = AssetBundle.LoadFromFile(Path.Combine(Application.dataPath, Path.Combine("AssetBundles", "example-prefab"))); if (moduleAB == null) { Debug.Log("Failed to load AssetBundle!"); return; } var prefab = moduleAB.LoadAsset<GameObject>("example-prefab"); Instantiate(prefab); } }
c#一个已正确加载材质的预制件
**注意:**使用 LZ4 压缩和未压缩的 AssetBundle 时,AssetBundle.LoadFromFile 仅在内存中加载其内容目录,而未加载内容本身。要检查是否发生了此情况,请使用内存性能分析器 (Memory Profiler) 包来检查内存使用情况。
C和D分别依赖材质T,如果T不单独打包,C和D每次打包的时候,都会和T一起打包,这样相当于C和D两个AB包中都有T,重复了。如果将T单独打包,那么C和D打包的时候就不用包含T,可是加载的时候,必须先加载T。不然就会没有材质。
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; public class LoadAB : MonoBehaviour { void Start() { //var materialsAB = AssetBundle.LoadFromFile("./AssetBundles/"+"mt_ab.u3d"); var moduleAB = AssetBundle.LoadFromFile("./AssetBundles/"+"sphere_ab.u3d"); if (moduleAB == null) { Debug.Log("Failed to load AssetBundle!"); return; } var prefab = moduleAB.LoadAsset<GameObject>("Sphere.prefab"); Instantiate(prefab); } }
可以使用四种不同的 API 来加载 AssetBundle。它们的行为根据加载捆绑包的平台和构建 AssetBundle 时使用的压缩方法(未压缩、LZMA 和 LZ4)而有所不同。
我们必须使用的四个 API 是:
AssetBundle.LoadFromMemoryAsync
此函数采用包含 AssetBundle 数据的字节数组。也可以根据需要传递 CRC 值。如果捆绑包采用的是 LZMA 压缩方式,将在加载时解压缩 AssetBundle。LZ4 压缩包则会以压缩状态加载。
以下是如何使用此方法的一个示例:
using UnityEngine;
using System.Collections;
using System.IO;
public class Example : MonoBehaviour
{
IEnumerator LoadFromMemoryAsync(string path)
{
AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return createRequest;
AssetBundle bundle = createRequest.assetBundle;
var prefab = bundle.LoadAsset<GameObject>("MyObject");
Instantiate(prefab);
}
}
但是,这不是实现 LoadFromMemoryAsync 的唯一策略。File.ReadAllBytes(path) 可以替换为获得字节数组的任何所需过程。
从本地存储中加载未压缩的捆绑包时,此 API 非常高效。如果捆绑包未压缩或采用了数据块 (LZ4) 压缩方式,LoadFromFile 将直接从磁盘加载捆绑包。使用此方法加载完全压缩的 (LZMA) 捆绑包将首先解压缩捆绑包,然后再将其加载到内存中。
如何使用 LoadFromFile
的一个示例:
using System.IO; using UnityEngine; public class LoadFromFileExample : MonoBehaviour { void Start() { var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle")); if (myLoadedAssetBundle == null) { Debug.Log("Failed to load AssetBundle!"); return; } var prefab = myLoadedAssetBundle.LoadAsset<GameObject>("MyObject"); Instantiate(prefab); } }
注意:在使用 Unity 5.3 或更早版本的 Android 设备上,尝试从流媒体资源 (Streaming Assets) 路径加载 AssetBundle 时,此 API 将失败。这是因为该路径的内容将驻留在压缩的 .jar 文件中。Unity 5.4 和更高版本则可以将此 API 调用与流媒体资源一起使用。
即将弃用(使用 UnityWebRequest)
此 API 对于从远程服务器下载 AssetBundle 或加载本地 AssetBundle 非常有用。这是一个陈旧且不太理想的 UnityWebRequest API 版本。
从远程位置加载 AssetBundle 将自动缓存 AssetBundle。如果 AssetBundle 被压缩,则将启动工作线程来解压缩捆绑包并将其写入缓存。一旦捆绑包被解压缩并缓存,它就会像 AssetBundle.LoadFromFile 一样加载。
如何使用 LoadFromCacheOrDownload
的一个示例:
using UnityEngine; using System.Collections; public class LoadFromCacheOrDownloadExample : MonoBehaviour { IEnumerator Start () { while (!Caching.ready) yield return null; var www = WWW.LoadFromCacheOrDownload("http://myserver.com/myassetBundle", 5); yield return www; if(!string.IsNullOrEmpty(www.error)) { Debug.Log(www.error); yield return; } var myLoadedAssetBundle = www.assetBundle; var asset = myLoadedAssetBundle.mainAsset; } }
由于在 WWW 对象中缓存 AssetBundle 字节所需的内存开销,建议所有使用 WWW.LoadFromCacheOrDownload 的开发人员都应该确保自己的 AssetBundle 保持较小的大小 - 最多只有几兆字节。此外,还建议在有限内存平台(如移动设备)上运行的开发人员确保其代码一次只下载一个 AssetBundle,以此避免内存峰值。
如果缓存文件夹没有任何空间来缓存其他文件,LoadFromCacheOrDownload 将以迭代方式从缓存中删除最近最少使用的 AssetBundle,直到有足够的空间来存储新的 AssetBundle。如果无法腾出空间(因为硬盘已满,或者缓存中的所有文件当前都处于使用状态),LoadFromCacheOrDownload() 将不会使用缓存,而将文件流式传输到内存中
为了强制执行 LoadFromCacheOrDownload,需要更改版本参数(第二个参数)。仅当传递给函数的版本与当前缓存的 AssetBundle 的版本匹配,才会从缓存加载 AssetBundle。
UnityWebRequest 有一个特定 API 调用来处理 AssetBundle。首先,需要使用 UnityWebRequest.GetAssetBundle
来创建 Web 请求。返回请求后,请将请求对象传递给 DownloadHandlerAssetBundle.GetContent(UnityWebRequest)
。GetContent
调用将返回 AssetBundle 对象。
下载捆绑包后,还可以在 DownloadHandlerAssetBundle 类上使用 assetBundle
属性,从而以 AssetBundle.LoadFromFile
的效率加载 AssetBundle。
以下示例说明了如何加载包含两个游戏对象的 AssetBundle 并实例化这些游戏对象。要开始这个过程,我们只需要调用 StartCoroutine(InstantiateObject())
;
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request
= UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");
Instantiate(cube);
Instantiate(sprite);
}
使用 UnityWebRequest 的优点在于,它允许开发人员以更灵活的方式处理下载的数据,并可能消除不必要的内存使用。这是比 UnityEngine.WWW 类更新和更优的 API。
现在已经成功下载 AssetBundle,因此是时候最终加载一些资源了。
通用代码片段:
T objectFromBundle = bundleObject.LoadAsset<T>(assetName);
T 是尝试加载的资源类型。
决定如何加载资源时有几个选项。我们有 LoadAsset
、LoadAllAssets
及其各自的异步对应选项 LoadAssetAsync
和 LoadAllAssetsAsync
。
同步从 AssetBundle 加载资源的方法如下:
加载单个游戏对象:
GameObject gameObject = loadedAssetBundle.LoadAsset<GameObject>(assetName);
加载所有资源:
Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();
现在,在前面显示的方法返回要加载的对象类型或对象数组的情况下,异步方法返回 AssetBundleRequest。在访问资源之前,需要等待此操作完成。加载资源:
AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync<GameObject>(assetName);
yield return request;
var loadedAsset = request.asset;
以及
AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync();
yield return request;
var loadedAssets = request.allAssets;
加载资源后,就可以开始了!可以像使用 Unity 中的任何对象一样使用加载的对象。
加载 AssetBundle 清单可能非常有用。特别是在处理 AssetBundle 依赖关系时。
要获得可用的 AssetBundleManifest 对象,需要加载另外的 AssetBundle(与其所在的文件夹名称相同的那个)并从中加载 AssetBundleManifest 类型的对象。
加载清单本身的操作方法与 AssetBundle 中的任何其他资源完全相同:
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
现在,可以通过上面示例中的清单对象访问 AssetBundleManifest
API 调用。从这里,可以使用清单获取所构建的 AssetBundle 的相关信息。此信息包括 AssetBundle 的依赖项数据、哈希数据和变体数据。
别忘了在前面的部分中,我们讨论过 AssetBundle 依赖项以及如果一个捆绑包对另一个捆绑包有依赖性,那么在从原始捆绑包加载任何资源之前,需要加载哪些捆绑包?清单对象可以动态地查找加载依赖项。假设我们想要为名为“assetBundle”的 AssetBundle 加载所有依赖项。
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[] dependencies = manifest.GetAllDependencies("assetBundle"); //传递想要依赖项的捆绑包的名称。
foreach(string dependency in dependencies)
{
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));
}
现在已经加载 AssetBundle、AssetBundle 依赖项和资源,因此是时候讨论如何管理所有这些已加载的 AssetBundle 了。
另请参阅:Unity 学习教程 - 管理已加载的 AssetBundle (Managing Loaded AssetBundles)
从活动场景中删除对象时,Unity 不会自动卸载对象。资源清理在特定时间触发,也可以手动触发。
了解何时加载和卸载 AssetBundle 非常重要。不正确地卸载 AssetBundle 会导致在内存中复制对象或其他不良情况,例如缺少纹理。
The biggest things to understand about AssetBundle management is when to call AssetBundle.Unload(bool) (or AssetBundle.UnloadAsync(bool)) and if you should pass true or false into the function call. Unload is a non-static function that will unload your AssetBundle. This API unloads the header information of the AssetBundle being called. The argument indicates whether to also unload all Objects instantiated from this AssetBundle.
AssetBundle.Unload(true)
卸载从 AssetBundle 加载的所有游戏对象(及其依赖项)。这不包括复制的游戏对象(例如实例化的游戏对象),因为它们不再属于 AssetBundle。发生这种情况时,从该 AssetBundle 加载的纹理(并且仍然属于它)会从场景中的游戏对象消失,因此 Unity 将它们视为缺少纹理。
假设材质 M 是从 AssetBundle AB 加载的,如下所示。
如果调用 AB.Unload(true),活动场景中的任何 M 实例也将被卸载并销毁。
如果改作调用 AB.Unload(false),那么将会中断 M 和 AB 当前实例的链接关系。
如果稍后再次加载 AB 并且调用 AB.LoadAsset(),则 Unity 不会将现有 M 副本重新链接到新加载的材质。而是将加载 M 的两个副本。
Generally, using AssetBundle.Unload(false)
does not lead to an ideal situation. Most projects should use AssetBundle.Unload(true)
and adopt a method to ensure that Objects are not duplicated. Two common methods are:
如果应用程序必须使用 AssetBundle.Unload(false)
,则只能以两种方式卸载单个对象:
如果不想自己管理加载资源包、依赖项和资源,可能需要使用 Addressable Assets 包。
如果调用 AB.Unload(true),活动场景中的任何 M 实例也将被卸载并销毁。
如果改作调用 AB.Unload(false),那么将会中断 M 和 AB 当前实例的链接关系。
如果应用程序必须使用 AssetBundle.Unload(false)
,则只能以两种方式卸载单个对象:
默认情况下,Unity 通过 LZMA 压缩来创建 AssetBundle,然后通过 LZ4 压缩将其缓存。本部分描述以上的两种压缩格式。
Unity 的 AssetBundle 构建管线通过 LZMA 压缩来创建 AssetBundle。此压缩格式是表示整个 AssetBundle 的数据流,这意味着如果您需要从这些存档中读取某个资源,就必须将整个流解压缩。这是从内容分发网络 (CDN) 下载的 AssetBundle 的首选格式,因为文件大小小于使用 LZ4 压缩的文件。
另一方面,LZ4 压缩是一种基于块的压缩算法。如果 Unity 需要从 LZ4 存档中访问资源,只需解压缩并读取包含所请求资源的字节的块。这是 Unity 在其两种 AssetBundle 缓存中使用的压缩方法。在构建 AssetBundle 以强制进行 LZ4(HC) 压缩时,应使用 BuildAssetBundleOptions.ChunkBasedCompression 值。
Uncompressed AssetBundles that Unity builds when you use BuildAssetBundleOptions.UncompressedAssetBundle require no decompression, but occupy more disk space. Uncompressed AssetBundles are 16-byte aligned.
为了使用 WWW 或 UnityWebRequest (UWR) 来优化 LZMA AssetBundle 的提取、再压缩和版本控制,Unity 有两种缓存:
将 AssetBundle 加载到内存缓存中会耗用大量的内存。除非您特别希望频繁且快速地访问 AssetBundle 的内容,否则内存缓存的性价比可能不高。因此,应改用磁盘缓存。
如果向 UWR API 提供版本参数,Unity 会将 AssetBundle 数据存储在磁盘缓存中。如果没有提供版本参数,Unity 将使用内存缓存。版本参数可以是版本号或哈希。如果 Caching.compressionEnabled 设置为 true,则对于所有后续下载,Unity 会在将 AssetBundle 写入磁盘时应用 LZ4 压缩。它不会压缩缓存中的现有未压缩数据。如果 Caching.compressionEnabled 设置为 false,Unity 在将 AssetBundle 写入磁盘时不会应用压缩。
最初加载缓存的 LZMA AssetBundle 所花费的时间更长,因为 Unity 必须将存档重新压缩为目标格式。随后的加载将使用缓存版本。
AssetBundle.LoadFromFile 或 AssetBundle.LoadFromFileAsync 始终对 LZMA AssetBundle 使用内存缓存,因此您应该使用 UWR API。如果无法使用 UWR API,您可以使用 AssetBundle.RecompressAssetBundleAsync 将 LZMA AssetBundle 重写到磁盘中。
内部测试表明,使用磁盘缓存而不是内存缓存在 RAM 使用率方面至少存在一个数量级的差异。因此,必须在内存影响、增加的磁盘空间要求以及应用程序的资源实例化时间之间进行权衡。
在缓存那里
如果向 UWR API 提供版本参数,Unity 会将 AssetBundle 数据存储在磁盘缓存中。如果没有提供版本参数,Unity 将使用内存缓存。版本参数可以是版本号或哈希。如果 Caching.compressionEnabled 设置为 true,则对于所有后续下载,Unity 会在将 AssetBundle 写入磁盘时应用 LZ4 压缩。它不会压缩缓存中的现有未压缩数据。如果 Caching.compressionEnabled 设置为 false,Unity 在将 AssetBundle 写入磁盘时不会应用压缩。
注意:此工具是 Unity 标准功能之外的额外功能。要访问此工具,必须从 GitHub 下载并安装,该过程独立于标准 Unity Editor 的下载和安装。
此工具使用户能够查看和编辑 Unity 项目的资源包的配置。它将阻止会创建无效捆绑包的编辑,并告知现有捆绑包的任何问题。此外还会提供基本的构建功能。
通过使用此工具,无需选择资源并在 Inspector 中手动设置资源包。它可以放入 5.6 或更高版本的任何 Unity 项目中。此工具将在 Window > AssetBundle Browser 中创建一个新菜单项。捆绑包的配置和构建功能在新窗口中拆分为两个选项卡。
注意:此实用程序处于预发布状态,因此我们建议在使用之前创建项目的备份。
此窗口提供了一个类似资源管理器的界面,用于管理和修改项目中的资源包。首次打开时,该工具将在后台解析所有捆绑包数据,缓慢标记所检测到的警告或错误。它会尽其所能与项目保持同步,但不能始终了解工具之外的活动。要强制快速刷新错误检测,或者使用外部更改来更新工具,请单击左上角的 Refresh 按钮。
该窗口分为四个部分:捆绑包列表、捆绑包详细信息、资源列表和资源详细信息。
左侧面板显示项目中所有捆绑包的列表。可用功能:
图标表示捆绑包是标准捆绑包还是场景捆绑包。
表示标准捆绑包的图标
表示场景捆绑包的图标
左下方面板显示捆绑包列表面板中的选定捆绑包的详细信息。此面板将显示以下信息(如果有):
右上方面板提供捆绑包列表中选择的任何捆绑包中包含的资源列表。可用功能:
请注意关于在捆绑包中包含文件夹的说明。可以将资源文件夹(从 Project Explorer)分配给捆绑包。在浏览器中查看时,文件夹本身将列为显式,而内容为隐式。这反映了用于将资源分配给捆绑包的优先级系统。例如,假设游戏在 Assets/Prefabs 中有五个预制件,然后将文件夹“Prefabs”标记为一个捆绑包,并将其中一个实际预制件(“PrefabA”)标记为另一个捆绑包。构建后,“PrefabA”将在一个捆绑包中,其他四个预制件将在另一个捆绑包中。
右下方面板可显示在资源列表面板中选择的资源的详细信息。此面板不具有交互性,但会显示以下信息(如果有):
可以将生成资源包数据的其他工具与浏览器集成。目前,主要的例子是资源包图形工具 (Asset Bundle Graph Tool)。如果检测到集成,则会在浏览器顶部附近显示一个选择栏。在此栏中可以选择默认数据源(Unity 的 AssetDatabase)或集成的工具。如果没有检测到,则选择器不显示,但可以通过右键单击选项卡标题并选择“Custom Sources”来添加工具。
Build 选项卡提供基本构建功能来帮助您开始使用资源包。在大多数专业情况下,用户最后需要更高级的构建设置。如果无法满足需求,任何人都可以使用此工具中的构建代码作为一个起点来编写自己的代码。界面:
1、逻辑实体分组:
a、一个UI界面或者所有UI界面一个包(这个界面里面所有的贴图和布局信息一个包);
b、一个角色或者所有角色一个包(这个角色里面的模型和动画一个包);
c、所有场景所共有的部分一个包(包括贴图和模型);
2、按照类型分组
所有的声音资源打成一个包,所有shader打成一个包,所有模型打成一个包,所有材质打成一个包。
3、按使用分组
把在某一时间内使用的所有资源打成一个包,可以按照关卡分,一个关卡所需要的所有资源包,包括角色、
贴图、声音等打成一个包,也可以按照场景分,一个场景所需要的资源一个包。
小结:
1、把经常更新的资源放在一个单独的包里、跟不经常更新的包分离。
2、把需要同时加载的资源放在一个包里面。
3、可以把其他包共享的资源放在一个单独的包里面。(做依赖,这样可以减少很多比较资源占用)
4、把一些需要同时加载的小资源打包成一个包。
5、如果对一个同一个资源有两个版本,可以考虑通过后缀来区分(如:v1 v2 v3 )(如unityAB_V1 、unityAV_V2);
**//=============================================================================================================**
小结:如图所示,其主要的特点是把模型预制体所使用的材质贴图进行整合,这样可以避免占用过多的内存资源,
**在加载时也可以避免包过大使用的时间太长,同时避免了资源的重复加载。**在需要加载时也可以通过依赖
关系进行逆向加载。
****//=============================================================================================================****
1、Build的路径(只要是在硬盘上都可以的)
2、BuildAssetBundleOptions(枚举类型)
a)、BuildAssetBundleOptions.None:使用LZMA算法压缩,压缩的包更小,但加载时间更长,
使用之前需要整体解压 。一旦被解压,这个包会使用LZ4重新压缩。使用资源的时候不需要整体解压
。在下载的时候可以使用LZMA算法。一旦它被下载了之后,它会使用LZ4算法保存到本地上。
b)、BuildAssetBundleOption.UncompressedAssetBundle (不压缩,包大,加载速度快)。
c)、*BuildAssetBundleOption.ChunkBasedCompression:(使用LZ4算法,压缩率没有LZMA高,*
*但我们可以加载指定资源的不用解压全部);*
*注意:使用LZ4算法,可以获得可以跟不压缩相媲美的加载速度,而且比不压缩的文件要小。*
3、BuildTarget:(选择Build出来的AB包需要的平台)
图解:
****//=============================================================================================================******
八:关于AssetBundle的卸载
首先为什么要把AB 包卸载了?其实也很简单,内存永远是有限的,当你转换一个场景或者关卡时,之前不需要的AB包所占用的内存是需要把它
释放掉的,这样才能让内存永远保持着一个健康的容量。
卸载主要有两个方面:
1、 减少内存使用,可以保证一个良好且健康的运行内存。
2、 有可能导致资源丢失问题。
所以一般什么时候卸载资源呢?
AssetBundle.Unload(true)卸载所有资源,即使有资源使用着(1,在关卡切换或场景切换时 2、资源没被用的时候调用);
AssetBundle.Unload(false)卸载所有没有被使用的资源。(这个适用时间比较多 ,可自行把控)。
**个别资源怎么卸载,通过Resources.UnloadUnusedAssets();
**
当场景切换时,unity会自行执行(Resources.UnloadUnusedAssets()这个方法);
****//=============================================================================================================******
九:关于AssetBundle的文件效验(每个AB包中有一个manifest文件中就有一个CRC)
**CRC、DM5、SHA1
**
相同点:
CRC、DM5、SHA1都是通过对数据进行计算,来生成一个效验值,该效验值用来效验数据的完整性。
不同点:
1、算法的不同。CRC采用多项式除法,MD5和SHA1使用的替换,轮转等方法。
**2、效验值的长度不同。CRC效验值的长度基本跟其多项式有关系,一般为16位或32位,**MD5是16个字节(128位);SHA1是20个字节(160位);
3、效验值的称呼不同。CRC一般叫做CRC值;MD5和SHA1一般叫哈希值(Hash)或散列值。
4、安全性不同。这里的安全性是指效验的能力,即数据的错误能通过效验位检测出来,CRC的安全性跟多项式有很大关系,相对于MD5和SHA1要弱很多;MD5的安全性很高,不过大概在04年的时候被山东大学的王小云破解了;SHA1安全性最高。
(算法的过程一般是不可逆的,破解即是逆向根据效验值推导出算法过程列表)
5、效率不同。CRC的计算效率很高;MD5和SHA1比较慢。
6、用途不同。CRC一般用作通信数据的效验,MD5和SHA1用于安全(Security)领域。比如文件效验、数字签名等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。