引用自:
https://www.cnblogs.com/CodeGize/p/8697227.html
https://www.jianshu.com/p/2a7c4a48aaee
---------------------------------- 帖子 1 ------------------------------------------
在unity3d中一般存在这么几种文件
- 资源文件(Imported Asset)
- 代码文件
- 序列化文件(Native Asset)
- 文本文档
- 非序列化文件
- meta文件
资源文件(ImportedAsset)
资源文件指一些创建好的,并且不再修改的文件。这样的文件一般是美术设计师,音频视频设计师创造的文件,比如FBX文件,贴图文件,音频文件,视频文件,动画文件(虽然动画文件可以被认为是配置文件,不过在由于一般不会去做修改,所以也认为是资源文件)。像这类文件,unity中都会在导入时进行转化。每一个类型都对应一个AssetImporter,比如AudioImporter,TextureImporter,ModelImport等等。在unity中点击这样的资源,在Inspector面板会出现设置界面,如下图所示是一个FBX的设置界面:
代码文件
代码文件包括所有的代码文件,代码库文件,shader文件等,在导入时,unity会进行一次编译。
序列化文件(Native Asset):
序列化文件通常是指unity能够序列化的文件,一般是unity自身的一些类型。比如prefab(预制体),unity3d(场景)文件,asset(ScriptableObject)文件.mat文件(材质球),这些文件能够在运行时直接反序列化为对应类的一个实例。
文本文档
文本文档比较特殊,它不是序列化文件,但是unity可以识别为TextAsset。很像资源文件,但是又不需要资源文件那样进行设置和转化。比如txt、xml文件等等。
非序列化文件
非序列文件是Unity无法识别的文件,比如一个文件夹也会被认为是一个文件,但是无法识别。
Meta文件
meta文件在unity中的作用非常关键,它有2个作用
- 定义在它同目录下,同名的非meta文件的唯一ID:GUID。而对于unity的序列化文件来说,引用的对象用的就是这个GUID。所以一旦meta中的GUID变更了,就要注意,它很可能引起一场引用丢失的灾难
- 存储资源文件的ImportSetting数据。在上文中资源文件是有ImportSetting数据的,这个数据正数存储在meta文件中。ImportSetting中专门有存储Assetbundle相关的数据。这些数据帮助编辑器去搜集所有需要打包的文件并分门别类。所以每一次修改配置都会修改meta文件。
GUID 和 MD5 是完全不同的两个东西,首先 guid 的概念只存在于 unity 体系,而 MD5 则是扩大到整个文件系统。
guid 用来识别不同的文件,它是根据 Path 路径来生成的;
而 MD5 是根据文件内容进行的算法,同一个文件 被修改前后 ,其 MD5 的值是不同 的,所以 MD5被称为“文件指纹”。
总结来说,guid 的因变量是 文件路径(当然也包括文件名),而MD5 的因变量是文件的内容(改文件名不会影响md5的值)。
Meta文件详解——Unity GUID/LocalID系统
meta文件实质上是一个文本文档,只是采用的是一种叫做 YAML 的格式来写的(https://docs.unity3d.com/Manual/FormatDescription.html)。
unity中的序列化文件都是用这个格式类写的,比如prefab,场景等。
我们使用nodepad++打开一个meta文件,然后在菜单中将语言设置为YAML,如下图所示:
这是一个asset文件(ScriptableObject)的meta文件。
GUID
guid是meta中最最最重要的数据。这个guid代表了这个文件,无论这个文件是什么类型(甚至是文件夹)。换句话说,通过GUID就可以找到工程中的这个文件,无论它在项目的什么位置。在编辑器中使用 AssetDatabase.GUIDToAssetPath和AssetDatabase.AssetPathToGUID进行互转。
所以在每次svn提交时如果发现有meta文件变更,一定要打开看一下。看看这个guid是否被更改。理论上是不需要更改的。
ImportSetting数据
后面比较重要的数据是ImportSetting数据。根据不同的文件类型,它的数据是不同的ImportSetting数据,比如上面的NativeFormatImporter,ModelImporter,AudioImporter等等。只要对照Inspector面板中的条目,都可以看懂每一行的意义。
所以知道这个之后,我们可以发现,假如我们把一个文件和这个文件的meta文件从一个Unity工程复制到另一个Unity工程中,它的配置是不会变的。(以前在2个工程手动裁剪同一个模型的20个动画真是傻到家了,直接将这个fbx和它的meta文件拷贝过去就行!)
FileID(LocalID)
有一个问题是,如果是一个图集,下面有若干个图片,那么,这个GUID怎么对应一个文件呢?是的,对于一个文件下有多个文件的情况,就需要另外一个ID来表示,这就是LocalID。更习惯用meta文件中的名字FileID。
FileID存储方式有2种
- 对于资源文件,非序列化文件,由于一般不会去更改源文件,所以FileID存储在meta文件中。以下是一个fbx文件的meta文件
- 对于序列化文件,自身数据里面会存储自身的FileID,也会记录所有子文件的FileID(更多关于序列化文件的数据,见下文)。比如对于这样的AnimatorController。它本身的数据如下,除了本身的FileID为9100000外,记录了4个AnimatorClip的FileID。而meta文件中只有自身的FileID
回到本节一开始的问题,如果是图集,因为是图片本身是资源文件,所以会有FileID存储在对应的meta文件中。打开任意一个图集,比如以下这个图集对应的meta文件
至此就是整个Unity的GUID/LocalID系统的基础了。通过GUID找到任何一个文件,通过FileID找到其中的某个子文件。
序列化文件详解——Unity文件引用系统
上文已经提到,对于所有的序列化文件,unity采用的是YAML来书写。所以对于一个unity3d(场景)文件,prefab文件,材质,控制器等,都可以用文本文档软件打开。这里还是用Notepad++打开。
为了能够简洁地说明问题,我们在unity中创建一个新的场景,然后创建2个Cube,一个做成Prefab。如图所示:
保存之后,用Notepad++打开这个1.unity3d。然后在菜单中设置语言为YAML。
可以看到大概的数据
- OcclusionCullingSettings裁剪数据(菜单Window->Occlusion面板中的数据)
- RenderSettings(菜单Window->Lighting->Settings面板中的部分数据)
- LightmapSettings(菜单Window->Lighting->Settings面板中的其他部分数据)
- NavMeshSettings(菜单Window->Navigation面板中的数据)
- 之后就是场景中的物件的数据
GameObject数据
展开第一个GameObject,可以看到这个的Name就是Main Camera。这个物体上有4个组件,一一对应下面的数据。这就是物体内的引用关系。每一个Unity对象都会有一个FileID,然后在需要引用时,使用这些FileID即可。所以在实例化一个这样的GameObject时,只要依照次序,依次创建物体,组件,初始化数据并进行引用绑定即可在场景中生成一个实例。
我们在Inspector面板中的右上角点击,然后选择Debug转成Debug模式下的Inspector面板
在Hierarchy面板中选中Main Camera可以看到如图所示,所有的组件的LocalIdentfierInFile的值就是刚刚在Notepad++中看到的数据
这里有一点,我们看到有一个叫做InstanceID的数据。这个是unity中一个实例的ID。每一个Unity实例都会有一个InstanceID。在运行时,可以使用UnityEngine.Object的GetInstanceID获取。但是要注意的是,每一次运行,相当于重新生成了新的实例,所以这个值是可变的。(更多细节参考《Unity编辑器下和运行时的加载过程》)
组件数据
在GameObject之后就是这个GameObject的组件数据(不知道次序会不会乱,理论上不影响)。每一个组件的数据基本上就是这个组件的一堆参数了。可以结合Unity中这个组件的面板来了解每一个数据的意义。
这里有一个问题,比如这里有一个组件是FlareLayer,但是在YAML里面只是一个Behaviour(所有Behaviour组件都看不到类型名字),怎么样才能知道他是一个FlareLayer?
可以看到在这个数据上方,在FileID左边我们看到一个124。对,这个就是FlareLayer。请参考YAML Class ID Reference,每一个unity类型都有一个对应的数字。
那么自定义脚本类呢?
我们创建一个Test脚本,继承MonoBehaviour。里面什么都不写。添加到Main Camera物体上。保存场景然后回到Notepad++。
可以看到多了一个MonoBehaviour,并且这个里面有一个m_Script数据,指向对应的GUID及其FileID。上文我们已经说了,任何一个文件都可以通过GUID找到,然后通过FileID找到它内部的子文件。所以这样就能识别出这个具体是什么类了。
我们往Test中写2个字段
public int A;
public Test RefTest;
在Main Camera中,设置Test脚本的A值为111,RefTest设置为自身。保存后回到Notepad++;
看到数据想必都明白了。
可以往Test中写一些其他类型的数据,看看这些序列化数据放在YAML的哪个位置!这里不再展开(这些数据和编辑器的SerializedProperty息息相关)
Prefab数据
在YAML的最下面有一个数据是Prefab数据
看起来很复杂,但是实际上,它就保存了最重要的几个数据
- Modification:每个组件的修改数据列表,但凡修改的数据,都会在这里体现。
- ParentPrefab:表示是哪一个Prefab。
所以上面的数据就是GUID为161b04a3180d83a4080b19801daaf356的Prefab,修改后的FileID为4040867914032966的一堆数据和FileID为1205630099335284的Name数据。
通过打开我们制作的Cube的Prefab文件及其meta文件,我们可以看到,meta文件中的GUID就是那个,而Prefab中存在4040867914032966(Transform)和1205630099335284(GameObject)
OK,至此,序列化文件的数据和引用的原理都已完毕
---------------------------------- 帖子 2 ------------------------------------------
重点来了:File GUID 及 Local ID。
File GUID
Unity会为每一个加入到Assets文件夹中的文件,创建一个同级同名的.meta文件,虽然文件类型的不同会影响这个.meta的具体内容,但它们都包含一个用来标记文件身份的File GUID。
例如,如果一个资源引用了另一个外部资源,比如一个Prefab引用了其他脚本、纹理或Prefab等,则一定会标明引用资源文件的File GUID。
Local ID
如果说File GUID表示为文件和文件之间的关系,那么Local ID表示的就是文件内部各对象之间的关系,打开一个*.Prefab文件可以很清晰的看到:
Unity通过Instance ID,来获取或判断一个对象是否已经被加载完毕。Instance ID由File GUID和Local ID转换而成,可以简单理解成是记录了资源所在内存地址的写着数字的钥匙牌。
每当Unity读入一个File GUID和LocalID时,就会自动将其转换成一个简单好记的数字牌,因为通过File GUID和Local ID定位资源的效率并没有直接解引用一个地址那么快。
如果发现这个牌上并没有挂着一把钥匙,表示当前这个这个资源还在磁盘中,尚不在内存里(没有加载);相反,如果这个牌子上有一把钥匙,表示这个资源已经被加载完毕,你可以快速的找到并使用它。
Unity会在项目启动后,创建并一直维护一张“映射表”,这张映射表记录的就是File GUID、Local ID以及由它们转换而成的Instance ID之间的关系,这样下次在请求资源时就可以快速的通过查看钥匙牌来获取资源了。
4、刚才的例子里,因为没有龙珠(资源没有加载),因此我们必须经历一场前往小巴黎的历险(LoadingAsset),而能够帮助我们准确定位北京通县的86版《中国地图》,可以近似理解成是Unity维护的一套将GUID和FileID解析为数据源地址的机制,这套机制中的信息,来自于:
(1) 场景加载时,Unity收集了与该场景关联的资源信息。
(2) 项目启动时,Unity收集了所有Resources文件夹下的资源信息。
(3) 读取AssetBundle时,Unity获取了AssetBundle文件的头部信息(Header)。
可以理解为:随着Unity知道更多的信息,这套机制将能够解析并定位更多的GUID和FileID。
5、当我们费劲千辛万苦找到龙珠后,记录在小本本上的7条位置,就好比7个能帮助够准确定位内存位置的Instance ID。想象一下,当我们下次再看到诸如《三颗龙珠召唤小神龙》这样的小诀窍(另外一个*.prefab),便可直接打开小本本(查询映射表中的Instance ID),对着编号及位置从书包里掏出龙珠(对InstanceID所指的内存地址进行解引用),啪啪啪一操作,小神龙这个游戏对象就能很快被召唤出来了,再也不用去什么通县了,可以节省大把时间,想想就觉的美滋滋呢。
每一次调用BuildPipleLine.BuildAssetBundles时,将会生成一批AssetBundle文件,具体数量根据传递AssetBundleBuild数组决定,每一个AssetBundleBuild对象将对应一个AssetBundle及一个同名+.manifest后缀文件。其中AssetBundle文件的后缀用户自行设置,比如".unity3d",".ab"等等;而.manifest文件是给人看的,里面有这个AssetBundle的基本信息以及非常关键的资源列表。
除了AssetBundleBuild数组所定的AssetBundle外,还将额外在output路径下生成的一对与output文件夹同名的文件及一个同名.manifest后缀文件。这个同名文件可厉害了,它记录了这批次AssetBundle之间的相互依赖关系。当然.manifest文件还是给人看的,我们可以用它分析资源间的依赖关系,但是在项目实际运行时,Unity并不会关心它。