type
status
date
summary
tags
category
icon

一、前言

在自己使用Unity的过程中,有时候需要通过资源的唯一ID来保存资源引用。这时候就会对Instance ID感到好奇,想要了解它的生成方式,所以趁闲暇时去了解和学习了相关知识,写下这篇文章记录。

二、Assets和Objects

首先看一下官方文档对于Assets和Objects的概念描述

2.1 Assets

Asset是在磁盘上的文件,存储在Unity项目的Assets文件夹下,材质,3D模型都是常见种类的Assets。一些Assets包含的数据能够被Unity原生支持,例如materials。其他的Assets需要经过处理变成原生的格式,例如FBX文件。

2.2 Objects

一个UnityEngine.Object或者Object是一系列序列化后的数据,用来描述一个资源的特别实例。它能够是Unity引擎中使用的任何种类的资源,例如mesh,sprite,AudioClip或者AnimationClip。所有的Objects都是UnityEngine.Object这个基类的子类。
尽管绝大多数Object类型都是被内置的,但是仍有两种特别的类型:
  • ScriptableObject:提供了一个方便的系统给开发者定义它们自己的数据类型。这些自定义的类型能够被Unity原生的序列化和反序列化,还能够被Unity编辑器中的Inspector窗口所控制
  • MonoBehaviour:提供了一个链接到MonoScript的包装。MonoScript是一个内部的数据类型,Unity 使用它来保存对特定程序集和命名空间中特定脚本类的引用。

2.3 区别和关联

Aseets和Objects之间是一种一对多的关系,一个Asset文件能够包含一个或者多个Objects。可以理解成一个预制体(Asset)里可能包含了多个组件(Object)

三、GUID和Local ID(file ID)

熟悉Unity的人知道,UnityEngine.Objects之间是可以互相引用的。这就会存在一个问题,这些互相引用的Objects有可能是在同一个Asset里,也有可能是在不同的Assets里。比如UGUI的一个Image需要引用一张Sprite Atlas里的Sprite。这就要求Unity必须有健壮的资源标识,能稳定的处理不同资源的引用关系。除此之外的话,Unity还必须考虑这些资源标识应该与平台无关,不能让开发者在切换平台的时候还需要关注资源的引用关系,毕竟它自己是一个跨平台部署的引擎。
基于这些特定的需求,Unity把序列化拆分成两个表达部分:File GUID和Local ID。

3.1 GUID

  • 定义
    • 在Unity文件系统中,GUID是全局唯一的资源定位器,提供了一个文件特殊位置的抽象。一旦一个文件和GUID绑定后,文件的名字和在磁盘上的存储位置就不再重要,文件可以自由移动而无需更新引用该文件的所有对象。场景,预制体和其他Assets通过GUID来唯一标识单个项目中和不同项目之间的文件。
       
      在以下场景中,生成GUID的情况有所不同
    • 首次将资源导入Unity项目时,Unity会自动为资源生成.meta文件,它与Asset存储在同一个目录中
    • 在Unity中移动Asset位置时,Unity会自动帮你同步.meta文件
    • 在Unity中复制Asset时,新生成的文件会获得新的GUID
    • Unity打开的情况下,单独删除.meta,Unity可以确保重新生成的GUID和现有的一样
    • Unity关闭的情况下,移动或者删除.meta文件,Unity无法恢复到原有的GUID,也就是说引用会丢失
    • 💡
      Unity中GUID工作原理 Unity在内部维护了一张资产GUID和路径的映射表,每当有新的资源进入工程,或者删除了某些资源。又或者调整了资源路径,Unity的编辑器都会自动修改这张映射表以便正确的记录资产位置。所以如果.meta文件丢失或者重新生成了不一样的GUID的话,Unity就会丢失引用,在工程内的表现就是某个脚本显示“Missing”,或者某些贴图材质的丢失导致场景出现粉红色
  • 详解
    • 在Asset同级目录下的.meta文件中,我们能够看到该Asset的GUID。meta文件实质上是一个文本文档,只是采用的是一种叫做 YAML 的格式来书写。Unity中的序列化文件都是用这个格式类写的,比如Prefab、场景等。打开一个名字为Circle的预制体文件如下:
      notion image
      在文件中,我们能看到Circle预制体的GUID为388e195e2960d4942b04cb8061c6e278,并且是通过PrefabImporter来导入的。
      非原生格式资源会在导入Unity时进行转化。每一个类型都对应一个AssetImporter,比如AudioImporter、TextureImporter、ModelImport等。在Unity中点击这样的资源,在Inspector面板会出现相应的设置界面
      如果与资产文件关联的GUID 丢失,则对该资产文件中所有对象的引用也将丢失。这就是为什么 .meta 文件必须保持与其关联的资产文件相同的文件名和文件夹中存储的原因。Unity 将重新生成已删除或放错位置的 .meta 文件。如果在 Unity 编辑器关闭时丢失了 .meta 文件,或者资源的路径发生了更改,而 .meta 文件没有随资源一起移动,则对该资源中对象的所有引用都将被破坏。
      能通过使用AssetDatabase.GUIDToAssetPathAssetDatabase.AssetPathToGUID相互转化
      💡
      何时去更改GUID 在正常情况下,你并不需要去改变文件的GUID。但是在某些特殊的情况下,例如具有不同 GUID 的相同文件(以及相关文件)出现在同一个(或新)项目中时,这是必要的。相关资料可以参考下面的文章:

3.2 Local ID(file ID)

一个图集中含有若干张不同的图片,如何区分图集中的不同图片呢?为了能够正确的区分它们,我们需要引入Local ID。
  • 定义
    • 任何一个Asset可能包含(或通过import产生)多个Object 资源,这个时候需要Local ID明确的区分不同的Object

四、Instance ID

  • 定义
    • GUIDs和Local IDs都是健壮性较好的,但GUID的比较速度较慢,运行时需要消耗更多性能。Unity内部维护一个缓存,它将GUIDs和Local IDs转化成简单的,唯一的整数。当新的Objects在缓存注册的时候,会被分配一个简单的,单调递增的整数,这就叫Instance ID。
  • 作用
    • 缓存维护了一个Instance ID,GUID和Local ID之间的映射,定义了对象的资源数据以及它的实例在内存中的位置,这允许UnityEngine.Objects稳定的维护彼此之间的引用。如果目标对象尚未被加载,则Unity可以通过GUID和Local ID定位到对象的资源数据,然后去实时的加载。
      在以下几种情况中,Unity会自动为Object添加或者删除Instance ID
    • 项目启动的时候,Instance ID缓存初始化所有的Objects(如场景中的引用),只要这些Objects在Resources文件夹
    • 在项目运行过程中,导入新的资产或者从AssetBundle中加载对象时,会将其添加到缓存中,即赋予Instance ID
    • 当从AB包卸载资产时,Unity将删除该对象的Instance ID,GUID和Local ID之间的映射以节省内存。如果重新加载AssetBundle,将为从重新加载的资产包加载的每个对象创建一个新的Instance ID

五、参考资料

 
如图片加载失败,可以刷新多试几次
 
Unity 笔记 PlayerPrefs使用Unity 资源管理模块 AssetBundle基础
  • Twikoo
  • Cusdis