GUID与fileID(本地ID)

Unity会为每个导入到Assets目录中的资源创建一个meta文件,文件中记录了GUID,GUID用来记录资源之间的引用关系。还有fileID(本地ID),用于标识资源内部的资源。资源间的依赖关系通过GUID来确定;资源内部的依赖关系使用fileID来确定。

InstanceID(实例ID)

Unity为了在运行时,提升资源管理的效率,会在内部维护一个缓存表,负责将文件的GUID与fileID转换成为整数数值,这个数值在本次会话中是唯一的,称作实例ID(InstanceID)。
程序启动时,实例ID缓存与所有工程内建的对象(例如在场景中被引用),以及Resource文件夹下的所有对象,都会被一起初始化。如果在运行时导入了新的资源,或从AssetBundle中载入了新的对象,缓存会被更新,并为这些对象添加相应条目。实例ID仅在失效时才会被从缓存中移除,当提供了指定文件GUID和fileID的AssetBundle被卸载时会产生移除操作。
卸载AssetBundle会使实例ID失效,实例ID与其文件GUID和fileID之间的映射会被删除以便节省内存。重新载入AssetBundle后,载入的每个对象都会获得新的实例ID。

资源的生命周期

Object从内存中加载或卸载的时间点是定义好的。Object有两种加载方式:自动加载与外部加载。当对象的实例ID与对象本身解引用,对象当前未被加载到内存中,而且可以定位到对象的源数据,此时对象会被自动加载。对象也可以外部加载,通过在脚本中创建对象或者调用资源加载API来载入对象(例如:AssetBundle.LoadAsset)
对象加载后,Unity会尝试修复任何可能存在的引用关系,通过将每个引用文件的GUID与FileID转化成实例ID的方式。
一旦对象的实例ID被解引用且满足以下两个标准时,对象会被强制加载:

  • 实例ID引用了一个没有被加载的对象。
  • 实例ID在缓存中存在对应的有效GUID和本地ID。

如果文件GUID和本地ID没有实例ID,或一个已卸载对象的实例ID引用了非法的文件GUID和本地ID,则引用本身会被保留,但实例对象不会被加载。在Unity编辑器中表现为空引用,在运行的应用中,或场景视图里,空对象会以多种方式表示,取决于丢失对象的类型:网格会变得不可见,纹理呈现为紫红色等等。

MonoScripts

一个MonoScripts含有三个字符串:程序集名称,类名称,命名空间。
构建工程时,Unity会收集Assets文件夹中独立的脚本文件并编译他们,组成一个Mono程序库。Unity会将Assets目录中的语言分开编译,Assets/Plugins目录中的脚本同理。Plugin子目录之外的C#脚本会放在Assembly-CSharp.dll中。而Plugin及其子目录中的脚本则放置在Assembly-CSharp-firstpass.all中。
这些程序库会被MonoScripts所引用,并在程序第一次启动时被加载。

资源文件夹Assets

为Unity编辑器下的资源文件夹,Unity项目编辑时的所有资源都将置入此文件夹内。在编辑器下,可以使用以下方法获得资源对象:

1
AssetDatabase.LoadAssetAtPath("Assets/x.txt");

注意:此方法只能在编辑器下使用,当项目打包后,在游戏内无法运作。参数为包含Assets内的文件全路径,并且需要文件后缀。
Assets下的资源除特殊文件夹内,或者在会打入包内的场景中引用的资源,其余资源不会被打入包中。

Resources资源载入

Assets下的特殊文件夹,此文件夹内的资源将会在项目打包时,全部打入包内,并能通过以下方法获得对象:

1
2
Resources.Load("fileName");   
Resources.Load("fileName");

注意:函数内的参数为相对于Resource目录下的文件路径与名称,不包含后缀。
Assets目录下可以拥有任意路径及数量的Resources文件夹,在运行时,Resources下的文件路径将被合并。
例:Assets/Resources/test.txt与Assets/TestFloder/Resources/test.png在使用Resource.Load (“test”)载入时,将被视为同一资源,只会返回第一个符合名称的对象。如果使用Resource.Load(“test”)将返回text.txt;
如果在Resources下有相同路径及名称的资源,使用以上方法只能获得第一个符合查找条件的对象,使用以下方法能或得到所有符合条件的对象:

1
2
Object[] assets = Resources.LoadAll("fileName");    
TextAsset[] assets = Resources.LoadAll("fileName");

在工程进行打包后,Resource文件夹中的资源将进行加密与压缩,打包后的程序内将不存在Resource文件夹,故无法通过路径访问以及更新资源。
在程序启动时会为Resource下的所有对象进行初始化,构建实例ID。随着Resource内资源的数量增加,此过程耗时的增加是非线性的。故会出现程序启动时间过长的问题,请密切留意Resource内的资源数量

卸载资源

所有实例化后的 GameObject 可以通过 Destroy 函数销毁。请留意 Object 与 GameObject 之间的区别与联系
Object 可以通过 Resources 中的相关 Api 进行卸载:

1
2
Resources.UnloadAsset(Object);			//卸载对应Object   
Resources.UnloadUnusedAssets(); //卸载所有没有被引用以及实例化的Object

注意以下情况:

1
2
3
4
5
Object obj = Resources.Load("MyPrefab");    
GameObject instance = Instantiate(obj) as GameObjct;
...
Destroy(instance);
Resources.UnloadUnusedAssets(); //卸载资源

此时UnloadUnusedAssets将不会生效,因为obj依然引用了MyPrefab,需要将obj = null,才可生效。

StreamingAssets

概述
StreamingAssets 文件夹为流媒体文件夹,此文件夹内的资源将不会经过压缩与加密,原封不动的打包进游戏包内。在游戏安装时,StreamAssets 文件件内的资源将根据平台,移动到对应的文件夹内。 StreamingAssets 文件夹在 Android 与 IOS 平台上为只读文件夹。

你可以使用以下函数获得不同平台下的StreamingAssets文件夹路径:

1
Application.streamingAssetsPath

参考以下各平台下 StreamingAssets 文件夹的等价路径,Application.dataPath 为程序安装路径。Android 平台下的路径比较特殊,请留意此路径的前缀,在一些资源读取的方法中是不必要的(eg:AssetBundle.LoadFromFile)

  • Application.dataPath+”/StreamingAssets”//Windows OR MacOS
  • Application.dataPath+”/Raw” //IOS
  • “jar:file://“+Application.dataPath+”!/assets/“ //Android
    StreamingAssets文件夹下的文件在游戏中只能通过 IOStream 或者 WWW 的方式读取
    io方式:
1
FileStream stream = File.Open(Application.streamingAssetsPath + "/fileName", FileMode.Open);

WWW方式(注意协议与不同平台下路径的区别):

1
2
3
4
5
6
7
private IEnumerator Start()   
{
WWW www = new WWW(Application.streamingAssetsPath + "/Cube");
yield return www;
//将获取的web页面的内容作为字符串返回(只读)
Debug.Log(www.text);
}

AssetBundle特有的资源加载方式:

1
2
//同步地从磁盘上的文件加载    
AssetBundle.LoadFromFile(assetbundlePath+"/name.unity3d");

PersistentDataPath

路径:Application.persistentDataPath
Unity指定的一个可读写的外部文件夹,该路径因平台及系统配置不同而不同。可以用来保存数据及文件。该目录下的资源不会在打包时被打入包中,也不会自动被Unity导入及转换。该文件夹只能通过IOStream以及WWW的方式进行资源加载。

WWW载入资源

WWW是一个Unity封装的网络下载模块,支持Http以及file两种URL协议,并会尝试将资源转换成Unity能使用的AssetsComponents(如果资源是Unity不支持的格式,则只能取出byte[])。具体对应的格式参考第一章表格。WWW加载是异步加载方法。
每次new WWW时,Unity都会启用一个线程去进行下载。通过此方式读取或者下载资源,会在内存中生成WebStream,WebStream为下载文件转换后的内容,占用内存较大。使用WWW.Dispose将终止仍在加载过程中的进程,并释放掉内存中的WebStream。 如果WWW不及时释放,将占用大量的内存。
如果载入的为Assetbundle且进行过压缩,则还会在内存中占用一份AssetBundle解压用的缓冲区Deompresion Buffer,AssetBundle压缩格式的不同会影响此区域的大小。
WWW.LoadFromCacheOrDownload静态方法

1
2
int version = 1;    
WWW.LoadFromCacheOrDownload(PathURL+"/fileName",version);

使用此方式加载,将先从硬盘上的存储区域查找是否有对应的资源,再验证本地Version与传入值之间的关系,如果传入的Version>本地Version,则从传入的URL地址下载资源,并缓存到硬盘,替换掉现有资源,如果传入的Version<=本地Version,则直接从本地读取资源;如果本地没有存储资源,则下载资源。此方法的存储路径无法设定以及访问。使用此方法载入资源,不会在内存中生成WebStream(其实已经将WebStream保存在本地),如果硬盘空间不够进行存储,将自动使用new WWW方法加载,并在内存中生成WebStream。在本地存储中,使用fileName作为标识符,所以更换URL地址而不更改文件名,将不会造成缓存资源的变更。保存的路径无法更改,也没有接口去获取此路径。


 评论