ET框架---ResourcesComponent学习笔记(AssetBundle!!)

本文详细介绍了Unity中AssetBundle的使用方法,包括如何加载和卸载AssetBundle,以及如何处理AssetBundle的依赖关系。文中还提供了实用的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ResourcesComponent学习笔记

请大家关注我的微博:@NormanLin_BadPixel坏像素


看这篇文章前,我希望大家先去了解一下Unity的AssetBundle。找最近的看,Unity近几个版本对其做了不少更新。

再详细的介绍一下Unity5的AssetBundle

并且,这里必定有热更新相关的资源读取,大家也一起学习一下吧。

大家可以先看一下ABInfo

ABInfo

我这里就直接注释源代码好了。

private int refCount;//用来标识这个资源被引用的次数

public string Name { get; }//用来识别Assetbundle
public AssetBundle AssetBundle { get; }//Assetbundle的具体资源

而且会在Dispose方法当中对存放的已经加载的AssetBundle资源进行释放。

ResourcesComponent

//用来存放已经加载了的Object
private readonly Dictionary<string, UnityEngine.Object> resourceCache = new Dictionary<string, UnityEngine.Object>();
//用来存放已经加载了的AssetBundle信息ABInfo
private readonly Dictionary<string, ABInfo> bundles = new Dictionary<string, ABInfo>();

// lru缓存队列。当想卸载一个包的时候,会先存放在这里而不是马上卸载
private readonly QueueDictionary<string, ABInfo> cacheDictionary = new QueueDictionary<string, ABInfo>();
public K GetAsset<K>(string bundleName, string prefab) where K : class
{
    string path = $"{bundleName}/{prefab}".ToLower();

    UnityEngine.Object resource = null;
    if (!this.resourceCache.TryGetValue(path, out resource))
    {
        throw new Exception($"not found asset: {path}");
    }

    K k = resource as K;
    if (k == null)
    {
        throw new Exception($"asset type error, type: {k.GetType().Name}, path: {path}");
    }
    return k;
}

因为导出AssetBundle的时候硬性设置全部都是小写,所以我们为了以防万一,在获取path的时候,把string给小写化。

这段代码就是通过AssetBundle的名字和想要获取的资源的名称获取到资源。

/// <summary>
/// 同步加载assetbundle
/// </summary>
/// <param name="assetBundleName"></param>
/// <returns></returns>
public void LoadBundle(string assetBundleName)

我们知道,一个AssetBundle可能会有多个依赖,我们在加载时候,要把这些依赖包一并加载进来。ResourcesHelper就是用来帮助我们获取依赖包的名称的。

ResourcesHelper

从这里开始,我们就要提出另一个问题了。当我们在开发的时候,不可能每次更新资源的时候都重新倒一遍AssetBundle,特别是当资源多了之后。所以我们得写两套资源加载的方案,一种用在我们开发的时候,直接从本地获取资源。另一种是我们在发布后,通过AssetBundle加载资源。而Unity的宏定义,就很方便我们使用。Define这个类,也是方便我们编辑的。因为如果只用宏定义,你会发现,当我们在编辑未定义的代码时,就是在编辑一段文本,享受不到脚本编辑器提供给我们强大的功能。当然不全是哈。

public static string[] GetDependencies(string assetBundleName)

这个方法就是通过上面的方案来获取到指定AssetBundle的依赖。

public static string[] GetSortedDependencies(string assetBundleName)

再此基础上,作者还提供了对依赖排序的方法。我们来看看是怎么做到的。

public static void CollectDependencies(List<string> parents, string assetBundleName, Dictionary<string, int> info)
{
    parents.Add(assetBundleName);
    string[] deps = GetDependencies(assetBundleName);
    foreach (string parent in parents)
    {
        if (!info.ContainsKey(parent))
        {
            info[parent] = 0;
        }
        info[parent] += deps.Length;
    }


    foreach (string dep in deps)
    {
        if (parents.Contains(dep))
        {
            throw new Exception($"包有循环依赖,请重新标记: {assetBundleName} {dep}");
        }
        CollectDependencies(parents, dep, info);
    }
    parents.RemoveAt(parents.Count - 1);
}

我们看到,这里有一个递归的用法。会不断的向下查找依赖,直到找到最底层。递归一般比较难理解,这里希望大家仔细看。第一次递归,如果一个AB有2个依赖AB1,AB2,则会登记AB有2个依赖,AB1 = 0,AB2 = 0。然后,再深入AB1,第二次递归,如果AB1有3个依赖AB1-1,AB1-2,AB1-3,然后更新信息,AB = 2 + 3 = 5,AB1 =0 + 3 = 3,AB2 = 0,AB1-1 = 0…然后深入AB2,然后。。。以此递归,直到需要深入的Assetbundle没有依赖,跳出递归。需要注意的是,用这种方法要求包内没有循环依赖。也就是不能AB1依赖AB2,AB2也依赖AB1。这样会进入死循环。

string[] ss = info.OrderBy(x => x.Value).Select(x => x.Key).ToArray();

运行到这里,我们已经知道,info里面已经存了所有依赖的AssetBundle,并且保存了每个AssetBundle所需要的依赖包个数为字典的值。info.OrderBy(x => x.Value) 就是通过字典的值进行从小到大的排序。.Select(x => x.Key).ToArray(); 更是提取出了排序后的键值,也就是依赖的AssetBundle名。这样,我们便可以让没有依赖的在最前面被加载,依赖项越少的排在越前面,后面加载的AssetBundle就不会出现依赖项没有被加载的情况了。这个逻辑大家仔细想想就可以明了了。很神奇的一步。

ResourcesComponent

public void LoadOneBundle(string assetBundleName)

这个方法就是直接加载指定的AssetBundle,一般调用这个方法的时候,都已经对其的依赖项加载过的。
如果可以在已经加载的bundles里面找到,则对其的引用信息加+1。如果在cacheDictionary里面,则把cacheDictionary里面的这个bundle取出来存到bundles内。引用+1。如果是第一次加载,就得加载AssetBunndle里面的资源了。如果我们是在编辑器模式下,也就是开发的时候,我们直接通过AssetBundle里面存放的资源地址加载资源。并把加载了的资源存放到resourceCache里面。之后,存放这个AssetBundle的信息进入bundles字典里。因为我们没有加载AssetBundle,所以这个ABInfo的AssetBundle是空的。

同样的,如果我们是通过AssetBundle获取资源,则先通过AssetBundle的名称加载到这个AssetBundle

AssetBundle assetBundle = AssetBundle.LoadFromFile(Path.Combine(PathHelper.AppHotfixResPath, assetBundleName));

这里通过PathHelper来决定路径。PathHelper没啥技术含量,固定写吧。

然后,把加载到的资源根据包名+资源名存入resourceCache。存放这个AssetBundle的信息进入bundles字典里。

需要注意的是,上面讲的是同步加载Asset资源的,作者还提供了异步的方法,没什么区别,只是换了加载资源的模式。

assets = await assetsLoaderAsync.LoadAllAssetsAsync();

经过了之前的.Net异步编程学习笔记,相信大家对此很好理解的吧。

讲完加载,我们再来看看卸载。卸载就不分同步异步啦。当然,卸载的同时也要把没有其他作用的依赖包一并卸载了。不过,这里我有一个疑惑,为什么作者这里要用

string[] dependencies = ResourcesHelper.GetSortedDependencies(assetBundleName);

获取排序好的依赖包信息呢?如果真要按排序,卸载的时候不是应该跟加载的时候相反,从依赖项多的开始卸载吗?如果不需要排序,为什么要获取排序好的呢?

我们先继续看下去吧。

private void UnloadOneBundle(string assetBundleName)

我们可以看到,当我们尝试卸载一个bundle的时候,会先把这个bundle的引用-1,如果这个bundle还有其他的引用,则不会卸载。如果没有引用了,则会先把它从bundles中移除,缓存进cacheDictionary。就像作者注释的,先缓存十个包。如果超过10个了,则把最先缓存的包真正卸载。

为什么要缓存呢?因为如果有一些包大量反复加载卸载的时候,会很耗性能。用这个机制就可以解决这个问题。

结束语

以前就想研究一下AssetBundle,看了一堆的资料,却还是不知道从哪里下手。看了作者的源代码后,豁然开朗。不过,还得研究一套规范的AssetBundle命名方法。

果然,看源代码才是最有用的。——————-Norman_林

本课程总体分为两大部分:理论篇与架构篇一: AssetBundle理论篇:     主要讲解Unity基于2017版本AssetBundle整体构建理论与详细示意图。     Unity原生打包、创建、下载、加载等步骤理论与具体代码实现方式。     **讲解AssetBundle中关于多包之间的依赖关系管理与解决方案。    二: AssetBundle架构篇:     1: 讲解Unity原生AssetBundle不适用工程化实战开发的原因与解决方案。     2: 详述AssetBundle框架设计原理图,以及核心设计理念。     3: Untiy编辑器界面全自动化创建AssetBundle与打包理念与代码实现。     4: 关于单一AssetBundle包的综合加载与管理以及相应测试实现。     5: AssetBundle整体管理。           本模块包含*.Manifest清单文件读取、AB包之间复杂依赖关系管理、整体场景化自动打包与加载管理流程、项目辅助全局定义与     路径管理等。 AssetBundle框架设计_理论篇配套资料下载链接链接:  https://pan.baidu.com/s/1bqtckZd 密码:ajgd 温馨提示:       本项目开发环境: Win10+Unity2017.1+VS2017版本。 操作系统与VS没有强制要求,但Unity学习版本必须Unity5.0以上版本(Unity4.x 以下不允许)。 一、热更新系列(技术含量:中高级):A:《lua热更新技术中级篇》https://edu.csdn.net/course/detail/27087B:《热更新框架设计之Xlua基础视频课程》https://edu.csdn.net/course/detail/27110C:《热更新框架设计之热更流程与热补丁技术》https://edu.csdn.net/course/detail/27118D:《热更新框架设计之客户端热更框架(上)》https://edu.csdn.net/course/detail/27132E:《热更新框架设计之客户端热更框架(中)》https://edu.csdn.net/course/detail/27135F:《热更新框架设计之客户端热更框架(下)》https://edu.csdn.net/course/detail/27136二:框架设计系列(技术含量:中级): A:《游戏UI界面框架设计系列视频课程》https://edu.csdn.net/course/detail/27142B:《Unity客户端框架设计PureMVC篇视频课程(上)》https://edu.csdn.net/course/detail/27172C:《Unity客户端框架设计PureMVC篇视频课程(下)》https://edu.csdn.net/course/detail/27173D:《AssetBundle框架设计_框架篇视频课程》https://edu.csdn.net/course/detail/27169三、Unity脚本从入门到精通(技术含量:初级)A:《C# For Unity系列之入门篇》https://edu.csdn.net/course/detail/4560B:《C# For Unity系列之基础篇》https://edu.csdn.net/course/detail/4595C: 《C# For Unity系列之中级篇》https://edu.csdn.net/course/detail/24422D:《C# For Unity系列之进阶篇》https://edu.csdn.net/course/detail/24465四、虚拟现实(VR)与增强现实(AR):(技术含量:初级)A:《虚拟现实之汽车仿真模拟系统 》https://edu.csdn.net/course/detail/26618五、Unity基础课程系列(技术含量:初级) A:《台球游戏与FlappyBirds—Unity快速入门系列视频课程(第1部)》 https://edu.csdn.net/course/detail/24643B:《太空射击与移动端发布技术-Unity快速入门系列视频课程(第2部)》https://edu.csdn.net/course/detail/24645 C:《Unity ECS(二) 小试牛刀》https://edu.csdn.net/course/detail/27096六、Unity ARPG课程(技术含量:初中级):A:《MMOARPG地下守护神_单机版实战视频课程(上部)》https://edu.csdn.net/course/detail/24965B:《MMOARPG地下守护神_单机版实战视频课程(中部)》https://edu.csdn.net/course/detail/24968C:《MMOARPG地下守护神_单机版实战视频课程(下部)》https://edu.csdn.net/course/detail/24979
本课程总体分为两大部分:理论篇与架构篇AssetBundle架构篇:     1: 讲解Unity原生AssetBundle不适用工程化实战开发的原因与解决方案。     2: 详述AssetBundle框架设计原理图,以及核心设计理念。     3: Untiy编辑器界面全自动化创建AssetBundle与打包理念与代码实现。     4: 关于单一AssetBundle包的综合加载与管理以及相应测试实现。     5: AssetBundle整体管理。          本模块包含*.Manifest清单文件读取、AB包之间复杂依赖关系管理、整体场景化自动打包与加载管理流程、项目辅助全局定义与路径管理等 一、热更新系列(技术含量:中高级):A:《lua热更新技术中级篇》https://edu.csdn.net/course/detail/27087B:《热更新框架设计之Xlua基础视频课程》https://edu.csdn.net/course/detail/27110C:《热更新框架设计之热更流程与热补丁技术》https://edu.csdn.net/course/detail/27118D:《热更新框架设计之客户端热更框架(上)》https://edu.csdn.net/course/detail/27132E:《热更新框架设计之客户端热更框架(中)》https://edu.csdn.net/course/detail/27135F:《热更新框架设计之客户端热更框架(下)》https://edu.csdn.net/course/detail/27136二:框架设计系列(技术含量:中级): A:《游戏UI界面框架设计系列视频课程》https://edu.csdn.net/course/detail/27142B:《Unity客户端框架设计PureMVC篇视频课程(上)》https://edu.csdn.net/course/detail/27172C:《Unity客户端框架设计PureMVC篇视频课程(下)》https://edu.csdn.net/course/detail/27173D:《AssetBundle框架设计_框架篇视频课程》https://edu.csdn.net/course/detail/27169三、Unity脚本从入门到精通(技术含量:初级)A:《C# For Unity系列之入门篇》https://edu.csdn.net/course/detail/4560B:《C# For Unity系列之基础篇》https://edu.csdn.net/course/detail/4595C: 《C# For Unity系列之中级篇》https://edu.csdn.net/course/detail/24422D:《C# For Unity系列之进阶篇》https://edu.csdn.net/course/detail/24465四、虚拟现实(VR)与增强现实(AR):(技术含量:初级)A:《虚拟现实之汽车仿真模拟系统 》https://edu.csdn.net/course/detail/26618五、Unity基础课程系列(技术含量:初级) A:《台球游戏与FlappyBirds—Unity快速入门系列视频课程(第1部)》 https://edu.csdn.net/course/detail/24643B:《太空射击与移动端发布技术-Unity快速入门系列视频课程(第2部)》https://edu.csdn.net/course/detail/24645 C:《Unity ECS(二) 小试牛刀》https://edu.csdn.net/course/detail/27096六、Unity ARPG课程(技术含量:初中级):A:《MMOARPG地下守护神_单机版实战视频课程(上部)》https://edu.csdn.net/course/detail/24965B:《MMOARPG地下守护神_单机版实战视频课程(中部)》https://edu.csdn.net/course/detail/24968C:《MMOARPG地下守护神_单机版实战视频课程(下部)》https://edu.csdn.net/course/detail/24979
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值