01Unity如何序列化资源和处理资源之间的引用关系

1. Asset和Object的区别

Asset
是磁盘上的文件,存储在Unity项目的资产文件夹中,纹理、模型或音频是常见的Asset,一些Asset中含有Unity原生格式的数据,例如材质,其他Asset则需要被转换成原生格式,例如FBX文件。

Object(UnityEngine.Object)用于描述某个资源的特定实例的序列化数据集合。它可以是由Unity引擎所使用的任何类型的资源,例如网格、精灵、音频和动画等,所有的Object都是UnityEngine.Object的子类

大多数的Object都是Unity内置类型,但有两种特殊的类型:

  • ScriptableObject是为开发者提供了一个便捷的自定义数据类型的系统。这些数据类型可以被Unity序列化和反序列化,并且可以在Unity编辑器的的窗口中操作
  • MonoBehaviour提供了对MonoScript的包装。MonoScript是Unity内部的数据结构,Unity通过它来持有对特定的程序集、命名空间下的脚本的引用。

2. Object间的引用

所有的Object都可以持有对其他Object的引用,被引用的Object可能在同一个Asset文件中,也可能在导入的其他Asset文件中。例如,一个材质Object通常含有对一个或者对多个纹理Object的引用,这些纹理的Object通畅从一个或多个Asset文件中导入

在序列化后,这些引用由两部分独立的数据组成:File GUIDLocal IDFile GUID标识Asset文件的目标资源的存储位置,Local ID标识Asset中的每个Object

File GUID存储在.mta文件中,在首次Asset导入Unity时会生成meta文件,他与Asset存储在同一个目录中。

3. 为什么要使用File GUID和Local ID

File GUID提供了对文件具体位置的抽象。只要某个File GUID可以关联到某个文件,那么这个文件在磁盘上的位置就不再重要,所以它可以被移动而不必更新所有引用这个文件的Object

任何Asset文件都可能包含或者通过导入而产生多个Object资源,因此需要Local ID来明确区分每个不同Object。如果某个Asset文件相关联的File GUID丢失了,那么这个Asset文建筑中所有对Object的引用也会丢失。因此meta文件必须与相关联的Asset文件同名,并且放在相同的文件夹中,Unity会重新生成被删除或者位置错误的meta文件。

4. 序列化与实例

尽管File GUID和Local ID足够稳健,但GUID对比速度慢,而且在运行时也需要一个更高效的系统。Unity在内部维护了一份缓存,它将FIle GUID和Local ID转换成简单的、会话唯一的整数。这些整数被称为Instance ID,当由新的Object注册到缓存时,Instance ID自增

缓存中维护了Instance ID、由File GUID和Local ID定义的Object源数据位置以及Object在内存中的实例之间的映射,这使Object之间可以维持文件的引用关系,解析Instance ID引用能够快速的返回已加载的Object,如果Object还没有被加载,Unity会通过File GUID和Local ID定位到Object的源数据,然后进行即时加载

在启动时,游戏会立即初始化包含所有被项目需要的Object的数据Instance ID缓存,以及所有在Resources文件夹中的Object的数据。在运行时倒入新的Asset或者从AssetBundle中加载Object时,会向缓存中新增条目。只有在提供具体File GUID和LOcalID的AssetBundle被卸载时,才会从缓存中移除Instance ID条目。这是原来的Instance ID、File GUID和Local ID会被删除,如果再次加载了这个AssetBundle,则会重新生成新的Instance ID

在一些特殊的平台上,某些事件会迫使Object被从内存中清除。例如,在iOS平台上,当程序被挂起(Suspend)时,图形Asset会被从显存中卸载。如果来自AssetBundle的Object被卸载了,Unity没办法重新加载这个Object的数据,现有的所有对该Object的引用都将失效。在前面的案例中,游戏画面中可能出现不可见的网格或者品红色的纹理。

5. 资源生命周期

Object会在下列时刻被自动加载:

  • 映射到该对象的Instance ID已取消引用
  • Object当前没有被加载到内存中
  • Object的源数据可以被定位

也可以通过创建对象或调用资源加载API(AssetBundle.LoadAsse),显示加载对象。加载对象时,Unity会尝试通过将每个引用的File GUID和Local ID转化为Instance ID来解决任何引用。如果两个条件为True,则首次取消应用对象实例ID时将按需加载Object:

  • Instance ID 引用当前未加载的Object
  • Instance ID具有有效的File GUID和在缓存中注册的Local ID

如果File GUID 和Local ID 没有Instance ID,或者具有卸载对象的Instance ID 引用无效的File GUID 和Local ID,则保留引用,但不会加载实际对象。这显示为 Unity 编辑器中的"Missing"引用。在正在运行的应用程序或场景视图中,"Missing"对象将以不同的方式可见,具体取决于其类型。例如,网格显示为不可见,而纹理可能显示为洋红色。

Object在三中特定方案中卸载:

  • 当未使用的Asset被清理时,将自动卸载Object。当场景以破坏性的方式发生更改(即当调用SceneManager.LoadScene 非增量加载),或者在脚本中调用Resources.UnloadUnusedAssets时被触发,这一过程中仅卸载没有被引用的Object,只有在没有Mono变量持有对该对象的引用,并且没有其他活动对象持有改对象的引用时,才卸载对象。另外,所有被标记为HideFlags.DontUnloadUnusedAsset和HideFlags.HideAndDontSave的对象都不会被卸载。
  • 通过调用Resources.UnloadAsset,可以显式卸载来自Resources文件夹下的Object。这些Object对象的Instance ID仍然有效,并且仍将包含有效的File GUID和Local ID条目。如果任何Mono变量或其他Object持有对使用Resources.UnloadAsset卸载的对象的引用,该对象将重新加载
  • 从AssetBundle获得的Object在调用 AssetBundle.unload(true) 时会自动立即卸载。这会使对象的Instance ID对应的File GUID 和Local ID无效,并且对卸载对象的任何实时引用都将变为"Missing"引用。在C#脚本中,尝试访问已卸载Object的方法或属性将会引发 NullReferenceException。

如果调用AssetBundle.Unload(false),从已卸载的AssetBundle生成的对象不会立即销毁,但Unity将其Instance ID所引用的File GUID 和Local ID失效。如果以后从内存中卸载这些对象,并且的一直引用卸载的对象,Unity无法重新加载这些对象

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值