UnityAB包学习笔记扩展

前言

        我之前有写一篇关于UnityAB包学习的笔记。在这篇笔记中,我额外安装了Asset Bundle Browser1.7.0。这个Unity官方在最近几年都没有更新了,我想大概率是Unity官方想推AA包吧。所以AB包的API其实没有太多的修改,因此之前笔记应该还没有过时。故,我并不想再多赘述一次,如果你没有关于使用AB包的经验,你可以先去看之前我写的笔记,然后再来看这篇文章。

        因为本人并没有参与过什么大型项目的游戏开发,因此下文的代码只是我个人的钻研。但是下文的代码还是有跑过项目的。只是这些项目几乎只有加载包中资源而没有卸载包资源的情况。

环境

    Windows10操作系统
    Unity 2022.3.8f1c1
    Asset Bundle Browser1.7.0

加载依赖

        AB包之间是存在依赖的情况,如果我们想正确加载一个包中的资源,那么我们要先将这个包所依赖的包加入其中。一般而言,我们只要能获取到AssetBundleManifest这个类型的变量,我们就可以获取到所有的依赖。示例代码如下:

1
2
3
4
AssetBundleManifest manifest;

// 获取所有的依赖
string[] strs = manifest.GetAllDependencies(abName);

因为是示例代码,你就默认上述代码段中的manifest已赋值。在使用这个的时候,我出现了一些疑问:如果一个资源在test1这个包中,而test2包资源A依赖了test1中的资源。那么test3包中的资源又使用了test2包中资源A。则test3包的依赖是如何呢?在Asset Bundle Browser操作界面的inspect下,我发现test3包依赖了test1test2test2依赖了test1。但是如果此时test1包又引用了test3中的资源呢?虽然这样意味着资源管理的问题,但是我还是想知道Unity这里的依赖会如何。经过我的实验,我发现test1确实会依赖test3。这就让我又想到了一种可能,如果test1包中引用了test3中的资源放到了test4包中时,test3的依赖中会加上test4吗?

        我在网上看到的加载AB包的代码中都存在缓存包的机制,即当这个包被加载后,这个包和这个包的依赖包就被存入缓存中。我在之前的文章里也用到了这样的做法,但是我并没有考虑到如果出现了上面这样依赖的情况。所以我的代码是这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void LoadDependencies(string abName)
{
if (cachedAB.ContainsKey(abName))
{
return;
}

// 获取所有的依赖
string[] strs = manifest.GetAllDependencies(abName);
foreach (string str in strs)
{
if (!cachedAB.ContainsKey(str))
{
cachedAB.Add(str, AssetBundle.LoadFromFile(Path.Combine(pathUrl, str)));
}
}
}

这时候我就有一个疑惑了。如果我先加载test1,因为其依赖test3,所以我test3也被加载。但是我会不会去加载test3的依赖包呢?如果不会,那么代码应该是下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void LoadPacketAndDependencies(string abName)
{
if (loadedAB.ContainsKey(abName))
{
return;
}

// 获取所有的依赖
string[] strs = manifest.GetAllDependencies(abName);

foreach (string str in strs)
{
if (!loadedAB.ContainsKey(str))
{
LoadPacketAndDependencies(str);
}
}

if(!loadedAB.ContainsKey(abName))
{
loadedAB.Add(abName, AssetBundle.LoadFromFile(Path.Combine(abPathURL, abName)));
}
}

但是当我打印出所有的包信息后,我发现其实不需要递归加载的操作。GetAllDependencies仍然会加载test3的依赖包,即使在其Inpsect窗口上没有体现出来。

卸载包

        资源卸载也是一门学问。可惜我现在的所参与的项目几乎不需要进行资源卸载。毕竟我参与的小游戏资源本来就没多少,而且还都是会重复利用的情况。所以资源卸载本身我就是很少用到的情况。虽然我还是留下了卸载包所需要的API,但是我仍然有些问题。如果我只是简单的调用了Unload函数,那么依赖于其的包还会怎样呢?我这里让test1依赖test4,然后卸载test4。测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System.IO;
using UnityEngine;

namespace Test
{
public class ABDependTest : MonoBehaviour
{
private void Start()
{
AssetBundle mainPackage = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "StandaloneWindows"));
AssetBundleManifest manifest = mainPackage.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

var test4 = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "test4"));
var test1 = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "test1"));

GameObject.Instantiate(test1.LoadAsset<GameObject>("testObj"));
test4.Unload(false);
GameObject.Instantiate(test1.LoadAsset<GameObject>("testObj"));
}
}
}

这里testObj引用了test4中的资源,但是我在卸载test4时并没有删除其已载入的物体。因此两个testObj都是正确的显示出来了。而当我将test4.Unload(false);改为test4.Unload(true);。这时候两个testObj都是错误的显示,如果我们先卸载test4再创建testObj也会是错误的显示。这也说明,当你卸载一个包的时候,所依赖于它的包是不会被一起卸载的。所以如果一个包,所依赖于其的包仍然要被使用时,这个包不应该被卸载。当然如果这个包所有的资源你都加载了,且在卸载的时候不清除。那么你当然可以删除这个包,但我仍然不建议这样。因为这样可能会导致,这些资源都不用时,你无法清除他们。

AB包使用的坑点

        在我使用AB包的时候,我发现了一个问题。如果我们把Timeline的资源放到AB包中,打包后的程序会丢失Timeline。你必须在打包前先选择一个带有Timeline的物体,然后打开Timeline的窗口。这样操作后才不会丢失。

闲言碎语

        这篇文章非常的水,大多数是我自己关于AB包的探索。当然碍于我个人浅薄的项目实践,我对AB包的使用探索也只能到此为止。不过这篇文章大概率就是上我自己个人的博客,水就水了吧。我自己的博客,那不是我想放什么就放什么,反正我又没有开通评论功能。大家又不能说我水。