大家好,又见面了,我是你们的朋友全栈君。
在 Inspector 底部,有 AssetBundle 的面板。
在 Assets 文件夹中创建一个名为 Editor 的文件夹,并将包含以下内容的脚本放在该文件夹中:
using System.IO;
using UnityEditor;
public class CreateAssetBundles {
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles() {
string assetBundleDirectory = "Assets/AssetBundles"; // 包的输出路径
if (!Directory.Exists(assetBundleDirectory)) {
// 若路径不存在,则创建
Directory.CreateDirectory(assetBundleDirectory);
}
// BuildPipeline:允许您以编程方式构建可从 Web 加载的播放器或 AssetBundle。
// BuildAssetBundles():打包,Build 出来的包是有平台限制的
BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
}
public static AssetBundleManifest BuildAssetBundles(string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);
关于构建 AB 后产生的文件
[构建 AB 后产生的文件](#构建 AB 后产生的文件)
将资源包上传到自己的服务器,以供游戏运行时的加载。
开发的时候一般会将 AB 包放在本地,因为会进行频繁的操作,发布的时候才会上传到服务器上。
using System.IO;
using UnityEngine;
public class LoadFromFileExample : MonoBehaviour {
private void Start() {
AssetBundle ab = AssetBundle.LoadFromFile("Assets/AssetBundles/scene/cubewall.u3d");
if (ab == null) {
Debug.Log("Failed to load AssetBundle!");
return;
}
// 加载包里的指定物体
var wallPre = ab.LoadAsset<GameObject>("CubeWall");
Instantiate(wallPre); // 实例化物体
}
// 加载包里的所有物体
private void LoadAllAssets(AssetBundle ab) {
Object[] objects = ab.LoadAllAssets();
foreach (var o in objects) {
Instantiate(o);
}
}
}
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
// GetAssetBundle(string, int):获取 AssetBundle 的位置以及要下载的捆绑包的版本。
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);
}
其他详细使用可见:
[AssetBundles 的具体使用](#AssetBundles 的具体使用 )
应根据实际项目的需求来进行设置,这里只是给一个简单的参考。
这里的分组策略不是指如何划分文件夹,而是,将哪些资源打包在一个 AssetBundle 里,一个 AssetBundle 就是一组。
逻辑实体分组非常适合于可下载内容 (DLC),因为通过这种方式将所有内容隔离后,可以对单个实体进行更改,而无需下载其他未更改的资源。
要构建供多个平台使用的 AssetBundle,类型分组是最佳策略之一。
例如,如果音频压缩在 Windows 和 Mac 平台上完全相同,则可以将所有音频数据打包到 AssetBundle 并重复使用这些捆绑包;而着色器往往使用更多特定于平台的选项进行编译,因此为 Mac 构建的着色器捆绑包可能无法在 Windows 上重复使用。此外,这种方法非常适合让 AssetBundle 与更多 Unity 播放器版本兼容,因为纹理压缩格式和设置的更改频率低于代码脚本或预制件。
总结
文件使用自定义后缀,包含在运行时为了加载资源而需要加载的内容。
此包的结构根据它是 AssetBundle 还是场景 AssetBundle 可能会略有不同。
对于生成的每个 AB 包,都会生成关联的清单文件。清单文件包含诸如 循环冗余校验 (CRC) 数据 和 包的依赖性数据 等信息。
对于普通 AssetBundle,它们的清单文件将如下所示:
AssetBundles.manifest – AB 包的清单文件
ManifestFileVersion: 0
CRC: 4225903359
AssetBundleManifest:
AssetBundleInfos:
Info_0:
Name: share.u3d
Dependencies: {}
Info_1:
Name: cubewall.u3d
Dependencies:
Dependency_0: share.u3d
Info_2:
Name: spherewall.u3d
Dependencies:
Dependency_0: share.u3d
share.u3d.manifest – 包含贴图资源
ManifestFileVersion: 0
CRC: 4044919538
Hashes:
AssetFileHash:
serializedVersion: 2
Hash: db0f6906b386d5d4413b2ddd1d9a6c61
TypeTreeHash:
serializedVersion: 2
Hash: 6f165f44e4778b6c9d85e7a145a54cb1
HashAppended: 0
ClassTypes:
- Class: 21
Script: {instanceID: 0}
- Class: 28
Script: {instanceID: 0}
- Class: 48
Script: {instanceID: 0}
Assets:
- Assets/Materials/Stone_floor_09.png
- Assets/Materials/Stone_floor_09.mat
Dependencies: []
cubewall.u3d.manifest – 依赖于 share
ManifestFileVersion: 0
CRC: 2862441256
Hashes:
AssetFileHash:
serializedVersion: 2
Hash: 6e48f6b8e6cedd4070323880d839a3ee
TypeTreeHash:
serializedVersion: 2
Hash: f49f05f36a566d50434f7d9f3fb347da
HashAppended: 0
ClassTypes:
- Class: 1
Script: {instanceID: 0}
- Class: 4
Script: {instanceID: 0}
- Class: 21
Script: {instanceID: 0}
- Class: 23
Script: {instanceID: 0}
- Class: 33
Script: {instanceID: 0}
- Class: 43
Script: {instanceID: 0}
- Class: 65
Script: {instanceID: 0}
Assets:
- Assets/Prefabs/CubeWall.prefab
Dependencies:
- G:/UnityDocuments/AssetBundle/Assets/AssetBundles/share.u3d
把共享资源放在一个包里,以节约空间。
如果 AssetBundle 中包含依赖项,则在加载尝试实例化的对象之前,务必加载包含这些依赖项的AB包。Unity 不会自动加载依赖项。
参考以下示例,a 中的材质引用了 b 中的纹理,加载 a 和 b 的顺序无关紧要,重要的是在使用 a 中的材质前应加载 b。
在此示例中,在从 a 加载材质之前,需要将 b 加载到内存中。加载 a 和 b 的顺序无关紧要,重要的是在使用 a 中的材质前应加载 b。
private void Start() {
AssetBundle.LoadFromFile("Assets/AssetBundles/share.u3d"); // 加载依赖包
AssetBundle ab = AssetBundle.LoadFromFile("Assets/AssetBundles/cubewall.u3d");
var wallPre = ab.LoadAsset<GameObject>("CubeWall");
Instantiate(wallPre); // 实例化物体
}
[当 AB 包存在依赖关系时,如何知道在加载原始包的资源前,需要加载哪些依赖包?](#加载 AssetBundle 清单)
根据 AssetBundle 是以什么样的形式提供的,而选择具体的加载方式。
此函数采用包含 AssetBundle 数据的字节数组。也可以根据需要传递 CRC 值。如果捆绑包采用的是 LZMA 压缩方式,将在加载时解压缩 AssetBundle。LZ4 压缩包则会以压缩状态加载。
using System.Collections;
using System.IO;
using UnityEngine;
public class LoadFromFileExample : MonoBehaviour {
private void Start() {
StartCoroutine(LoadFromMemoryAsync("Assets/AssetBundles/cubewall.u3d"));
}
IEnumerator LoadFromMemoryAsync(string path) {
// 加载 AB
AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); // 字节数组
yield return createRequest;
AssetBundle bundle = createRequest.assetBundle;
// 使用里面的资源
var prefab = bundle.LoadAsset<GameObject>("CubeWall");
Instantiate(prefab);
}
}
using System.Collections;
using System.IO;
using UnityEngine;
public class LoadFromFileExample : MonoBehaviour {
private void Start() {
StartCoroutine(LoadFromFile("Assets/AssetBundles/cubewall.u3d"));
}
IEnumerator LoadFromFile(string path) {
// 加载 AB
AssetBundleCreateRequest createRequest = AssetBundle.LoadFromFileAsync("Assets/AssetBundles/cubewall.u3d");
yield return createRequest; // 等待加载完成
AssetBundle ab = createRequest.assetBundle;
// 使用里面的资源
var prefab = ab.LoadAsset<GameObject>("CubeWall");
Instantiate(prefab);
}
}
从远程服务器加载 AssetBundle
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
public class LoadFromFileExample : MonoBehaviour {
private void Start() {
StartCoroutine(InstantiateObject());
}
IEnumerator InstantiateObject() {
// 加载 AB
//string uri = @"http://127.0.0.1/AssetBundles/cubewall.u3d";
string uri = "file:///G:/UnityDocuments/AssetBundle/Assets/AssetBundles/cubewall.u3d";
UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.SendWebRequest();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
// 使用里面的资源
var prefab = ab.LoadAsset<GameObject>("CubeWall");
Instantiate(prefab);
}
}
通用代码片段:
// T 是尝试加载的资源类型
T objectFromBundle = bundleObject.LoadAsset<T>(assetName);
同步加载方式:
// 加载单个游戏对象
GameObject gameObject = loadedAssetBundle.LoadAsset<GameObject>(assetName);
// 加载所有资源
Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();
异步加载方式:
// 加载单个游戏对象
AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync<GameObject>(assetName);
yield return request;
var loadedAsset = request.asset;
// 加载所有资源
AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync();
yield return request;
var loadedAssets = request.allAssets;
当 AB 包存在依赖关系时,如何知道在加载原始包的资源前,需要加载哪些依赖包?
可以通过 Manifest 文件得到某个包的依赖,清单对象可以动态地查找加载依赖项。
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
public class LoadFromFileExample : MonoBehaviour {
private void Start() {
// 加载 ab 包
AssetBundle manifestAB = AssetBundle.LoadFromFile("Assets/AssetBundles/AssetBundles");
// 加载清单文件
AssetBundleManifest manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 加载 ab 的所有依赖项
string[] dependencies = manifest.GetAllDependencies("wall.u3d");
foreach (string dependency in dependencies) {
print(dependency);
AssetBundle.LoadFromFile("Assets/AssetBundles/" + dependency);
}
// 加载资源
AssetBundle ab = AssetBundle.LoadFromFile("Assets/AssetBundles/wall.u3d");
var wallPre = ab.LoadAsset<GameObject>("CubeWall");
// 实例化物体
Instantiate(wallPre);
}
}
了解何时加载和卸载 AssetBundle 非常重要。不正确地卸载 AssetBundle 会导致在内存中复制对象或其他不良情况,例如缺少纹理。
// 卸载 AssetBundle
AssetBundle.Unload(bool);
假设材质 M 是从 AssetBundle AB 加载的,如下所示。
通常,使用 AssetBundle.Unload(false) 不会带来理想情况。大多数项目应该使用 AssetBundle.Unload(true) 来防止在内存中复制对象。
大多数项目应该使用 AssetBundle.Unload(true) 并采用一种方法来确保对象不会重复。两种常用方法是:
如果应用程序必须使用 AssetBundle.Unload(false),则只能以两种方式卸载单个对象:
修补 AssetBundle 很简单,只需要下载新的 AssetBundle 并替换现有的 AssetBundle。
如果使用 UnityWebRequest 来管理应用程序的缓存 AssetBundle,则将不同的版本参数传递给所选 API 将触发新 AssetBundle 的下载。
在修补系统中要解决的更难的问题是检测要替换的 AssetBundle。修补系统需要两个信息列表:
修补程序应下载服务器端 AssetBundle 列表并比较这些 AssetBundle 列表。应重新下载缺少的 AssetBundle 或已更改版本控制信息的 AssetBundle。
也可以编写一个自定义系统来检测 AssetBundle 的更改。自己编写系统的大多数开发人员会选择对 AssetBundle 文件列表使用行业标准数据格式(例如 JSON)和并使用标准 c sharp 类(例如 MD5)来计算校验和。
若不指定 Sprite 的 Packing Tag
,这些 Sprite 将会被自动打包在一个图集里面。
假如 a 包使用了这个图集里的一张图片,那么这个图集就会被打包在 a 包里面,如果 b 包也使用了图集里的一张图片,那么这个图集也会被打包在 b 包里面,这样一来就产生了图集的重复问题。
为了确保精灵图集不重复,请确保将相同精灵图集的所有精灵分配到同一个 AssetBundle。
由于 Android 生态系统中存在严重的设备碎片,因此通常需要将纹理压缩为多种不同的格式。虽然所有 Android 设备都支持 ETC1,但 ETC1 不支持具有 Alpha 通道的纹理。如果应用程序不需要 OpenGL ES 2 支持,解决该问题的最简单方法是使用所有 Android OpenGL ES 3 设备都支持的 ETC2。
解决这个问题的一种方法是使用 Unity 5 的 AssetBundle 变体。(有关其他方案的详细信息,请参阅 Unity 的 Android 优化指南。)
下载后,直接将 Editor 文件夹放在自己的项目目录下,此工具使用户能够查看和编辑 Unity 项目的资源包的配置。此工具将在 Window 菜单下创建 AssetBundle Browser 菜单项。
此窗口提供了一个类似资源管理器的界面,用于管理和修改项目中的资源包。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/148014.html原文链接:https://javaforall.cn