专栏首页重归混沌谈谈Unity的资源管理

谈谈Unity的资源管理

在Unity最佳实践明确指出, 要使用AssetBundle而不是Resources目录来管理资源。

然而,事情并不像Unity官方描述的那么美好。因为使用AssetBundle我们甚至无法实现一个易用的,完备的资源管理方案。

据Unity官方说,一般有两种方案。

方案一,如果你的游戏是关卡性质的,可以在一个关卡里加载所有AssetBundle,然后在进入下一关卡时,卸载本关卡中加载的所有AssetBundle. 但这种机制似乎只对愤怒的小鸟这种小游戏才适用吧:D。

方案二,如果你的游戏不是关卡类的,那么Unity推荐做一个资源对AssetBundle引用计数。

如果一个对象(Asset或其他AssetBundle)引用此AssetBundle则其引用计数加1. 如果此AssetBundle首次加载(即加载前引用计数为0), 还需要递归对其依赖引用计数加1。

如果一个AssetBundle的引用计数为0则释放这个AssetBundle,同时还需要递归对其依赖引用计数减1.

除非,我们做像愤怒小鸟一样的通关游戏,不然似乎只有方案二给我们用。而且方案二乍一看是完备的,因为这正是GC算法的一种实现。

但是如果稍微仔细思考一下就会发现,这个方案只是AssetBundle的管理方案,是个半成品,要如何管理管理资源之间的依赖,Unity却只字未掉,看起来是让用户自己想办法,这似乎与其易学易用的宗旨不太相符。

下面来分析一下Unity中资源之间的关系。

在Unity中资源大约分为以下几种:纹理(Texture)、网格(Mesh)、动画片段(AnimationClip)、音频片段(AudioClip)、材质(Material)、着色器(Shader)、字体资源(Font)以及文本资源(TextAsset)。

AssetBundle中还有一个极其特殊的存在,那就是Prefab, AssetBundle.LoadAsset时返回的是GameObject, 但是又必须经过Instantitate之后变成另外一个GameObject才能使用。此后所说的GameObject均是Instantitate之后的GameObject。

GameObject可以添加各种Component来引用上述除资源,还可以通过代码动态增减某个GameObject上的Component或者修改Component对资源的引用。这种灵活性给资源管理带来了巨大麻烦,而没有这种灵活性,逻辑的实现就会更麻烦。


下面,举例来说明一下,要正确管理GameObject和资源之间的引用关系有多么艰难。

Prefab P能过Instantitate生成A,B,C,D四个GameObject.

执行如下代码之后,A引用{P,T1}, B引用{P,T1}, C引用{P,T3}。并且T2应该被Unload。

1: A.GetComponent<SpriteRender>().sprite = (Sprite)T1; 2: B.GetComponent<SpriteRender>().sprite = (Sprite)T1; 3: C.GetComponent<SpriteRender>().sprite = (Sprite)T2; 4: C.GetComponent<SpriteRender>().sprite = (Sprite)T3;

要想自动正确的管理GameObject和资源的引用关系,就必须要感知到对GameObject的赋值操作。

例如:所有的sprite赋值都必须使用类似SpriteAssign(SpriteRender sr, Sprite s)的接口。

SpriteAssign的执行流程通常是这样的。

  1. 检查sprite的值是不是T1相同,如果是相同则不做处理
  2. 检查sprite的值是不是从P中clone过来的,如果不是,将此sprite的引用计数减1
  3. 将T1的引用计数加1

如果P是一个树状态结构,即有P–(child)–>p1–(child)–>p2。

1: A.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T1; 2: B.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T1; 3: C.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T2; 4: C.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T3;

SpriteAssign接口中的步骤2就显得格外复杂,它必须修正引用关系如下:A引用{P,T1}, B引用{P,T1}, C引用{P,T3}。

同时Destory操作也要被感知,如果Destory(A)则需要释放A引用的资源,而如果Destory(A.p1.p2)则需要修正A对资源的引用情况。因为此时的引用关系是,A引用{P}。换句话说Destroy的开销也会变大。

而赋值和Destory都算不上低频操作,尤其是赋值操作。这样的开销已经足够让程序慢上好几倍了。如果不能承受这些开销,全自动化资源管理是不可能实现的。

我想这也是Unity不默认提供一套标准的全自动化资源管理方案的根本原因吧。


受方案一的启发,我觉得可以通过如下接口做一个半自动化的资源管理器。

void level.open();
void level.close();
void level.dispose();
void stack.push() {
    level l = new level()
    l.open()
    push l in to stack
}
void stack.pop() {
    pop l from stack
    l.dispose()
}

每一个level对象都会记录在level.open()和level.close()之间所有加载过的资源,加过载多少次就记录多少次,这些资源会在执行level.dispose()时如实的进行释放。

其中stack在管理UI资源方面几乎已经达到了全自动化,当你打开一个UI时调用stack.push,在退出此UI时调用stack.pop会自动释放在此UI期间你所加载的全部资源。

而在其他不具有栈式加载资源特征的地方,level类也提供了一种方便的半自动化管理方案。

最重要的是,此种方案的开销和复杂度,都要远低于全自动化管理方案。

本文分享自微信公众号 - 重归混沌(findstrx),作者:重归混沌

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 移动平台native代码遭遇的坑

    最近客户端终于开始运行在移动平台上了,之前在PC平台上完全没问题的代码,开始出现一些诡异的问题。

    重归混沌
  • 一次并发Bug

    写了一段代码,开了6000个客户端,去并发RPC请求,服务端收到RPC之后会随机10~2500ms 延时之后再返回。丢到我的VPS上之后,也没在意,过了两天就忘...

    重归混沌
  • 再谈分布式服务架构

    在两年前,我曾经设计过一版,高可伸缩服务器架构, 但只进行了理论推演,并没有使用具体业务逻辑验证过。以这两年的经验来看,这个架构不具备可实施性。

    重归混沌
  • 美国警察暴力执法有多严重? | Alfred数据室

    作为生活在大洋此岸的我们,对于美国警察暴力执法也时有耳闻。为什么这次事件会引起那么大的波动呢?美国警察暴力执法情况有多严重?暴力执法之后不需要负责任的吗?是否真...

    Alfred数据室
  • 代码安全之上传文件

    从数据包中可以看出,验证文件类型的参数有:Content-Type、Filename、Filedata。

    信安之路
  • 【计算机网络】TCP通信的细节及TCP连接对HTTP事务处理性能影响

    从三次握手的细节说起 刚开始尝试使用java等后端语言写IO流,或用套接字(socket)实现简单C/S通信的同学们,常常会接触到的一个概念:就是所谓的“三次握...

    外婆的彭湖湾
  • Python闭包及装饰器运行原理解析

    闭包从形式上来说是在外部函数中定义内部函数,并且内部函数引用了外部函数的变量,此变量叫做自由变量。

    砸漏
  • [TCP/IP] 传输层-TCP和UDP的使用场景

    传输层-TCP和UDP应用场景 TCP(传输控制协议) 需要将要传输的文件分段传输,建立会话,可靠传输,流量控制 UDP(用户报文协议) 一个数据包就能完成...

    陶士涵
  • 如何通过审计安全事件日志检测密码喷洒(Password Spraying)攻击

    许多渗透测试人员和攻击者通常都会使用一种被称为“密码喷洒(Password Spraying)”的技术来进行测试和攻击。对密码进行喷洒式的攻击,这个叫法很形象,...

    伍尚国
  • AJAX

    XMLHttpRequest对象有一个onreadystatechange事件,可以监听这五个状态,它会在XMLHttpRequest对象的状态发生变化时被调用...

    py3study

扫码关注云+社区

领取腾讯云代金券