GetInstanceID
是 Unity 中 Object
类的一个方法,它用于获取一个对象的唯一实例标识符。每个 Unity 对象(如游戏对象、组件、资源等)都有一个唯一的实例 ID,这个 ID 在对象的生命周期内是唯一的。
对于它的生命周期是不确定的。网上说在切换场景或者编辑器关闭重启后会变。这些说法太片面,或者说根本不对。
下面做说明
首先在编辑器里在属性面板里点击右上角三个小点点切换到debug模式
然后随便选择一个资产(脚本也是资产的一种),将会看到InstanceId
这个资产InstanceId是在编辑器启动时为每个资产分配的Id,他的生命周期是和编辑器生命周期一样的。即只有在关闭编辑器然后重启后才会重新分配发生变化,换句话说,只要编辑器不关闭,这个资产InstanceID就不会变。这个你可以重启编辑器测一下就知道。
所以切换场景会发生变化这是错误的。但是切换场景会影响实例化的InstanceID而不是上面资产的InstanceId。后面会详细说明。
-
加载资源:
- 当你使用
AssetDatabase.LoadAssetAtPath
或Resources.Load
加载资源时,你获取的是原始资产的引用。这个原始资产在编辑器中有一个唯一的实例InstanceID,这个InstanceID 是在原始资产创建时分配的。加载资产已分配的InstanceID,说白了加载时只是从资源管理器对应的文件里拿去资源数据包括InstanceID - 如下面代码
-
// 加载一个资源对象,即原始资源 GameObject prefab = Resources.Load<GameObject>("MyPrefab");
此时的prefab就是资源管理器的资源文件的原始资源,所以在编辑器运行的生命周期里一直不会变。类似的还有
AssetDatabase.LoadAssetAtPath也是一样的。此时的originPrefab的InstanceId和编辑器的时一摸一样的,不会变,因为此时加载的就是编辑器的资源数据。除非你删除资源,删除资源InstanceId会被回收,有人说掉用destroy方法销毁怎么样,呵呵,那是不行的直接报错。还有另一个销毁方法DestroyImmediate(originPrefab );也是不行,除非传入true,DestroyImmediate(originPrefab ,ture),但是传入true就删除资源一样的操作
- 当你使用
-
实例
InstanceId
的稳定性:- 在编辑器的生命周期内,原始资产的实例
InstanceId
是稳定的,即使你在编辑器中切换场景或进行其他操作,原始资产的实例InstanceId
也不会变化。只有在编辑器关闭或重新启动时,原始资产的实例InstanceId
才可能被重新分配。
- 在编辑器的生命周期内,原始资产的实例
-
删除原始资产:
- 如果你从
Assets
文件夹中删除一个原始资产文件,那么它的实例InstanceId
也会被回收,因为这个资产已经不再存在。此时,原始资产的引用会失效,不能再通过加载方法访问该资产。
- 如果你从
- 资产的原始资源是不会变的。但是但资产通过Object.Instance或者继承自Object间接调用Instance方法的实例化对象会分配新的InstanceId,当然Instance方法很多情况传入的时原始资源。如下面代码
-
//加载一个资源对象,即原始资源,此时的originPrefab的InstanceId和编辑器的时一摸一样的,不会变,除非你删除资源,删除资源InstanceId会被回收,有人说掉用destroy方法销毁怎么样,呵呵,那是不行的直接报错。还有另一个销毁方法DestroyImmediate(originPrefab );也是不行,除非传入true,DestroyImmediate(originPrefab ,ture),但是传入true就删除资源一样的操作 GameObject originPrefab = Resources.Load<GameObject>("MyPrefab"); GameObject entity=Object.Instance(originPrefab );
这种操作originPrefab也是不会变的,也为他就是原始资源,通过 Object.Instance获取新对象entity时,方法会为entity分配新的
InstanceId,不会改变
originPrefab的InstanceId,在每次Object.Instance都会分配一个新的,但是如果
entity被销毁后InstanceId会被回收,如果在回收后再Object.Instance获得
entity可能会和原来的对象InstanceId是同一个。
-
这时候,如果切换的场景所有的实例化后的对象(注意不是加载的原始资产)都会被销毁,InstanceId会被回收。然后在重新实例化后,InstanceId会重新分配,造成实例化后的对象InstanceId变化。就是说同一个原始资产实例化化后的对象InstanceId是会变的。但是原始资产不会变。
-
总结
-
通过AssetDatabase.LoadAssetAtPath
或Resources.Load
加载获得的对象就是资源管理器文件对象的InstanceId,因为对象是一个东西。
-
只有通过
Object.Instance或者继承自Object间接调用Instance方法的实例化对象会分配新的InstanceId,获得不同的InstanceId,资产原始文件对象是不会改变影响的。 -
InstanceId是很有用的。
AssetDatabase.LoadAssetAtPath
或Resources.Load加载的资源文件对象会加载到内存里不会被销毁。基础如图片资源加载,可以在获取原始资源后调用
Resources.UnloadAsset(Object)传入原始对象资源,是可以销毁内存资源的,不能对原始资源使用Destoy方法。但是对于prefab资源加载是致命的,因为prefab会依赖其他资源,在加载是unity会默认加载这些依赖的资源。我们是无法获取 那些资源的。也是最严重的就是prefab,在unity中,原始资源prefab是无法通过Resources.UnloadAsset回收的,会报错。调用 Resources.UnloadUnusedAssets()也是没效果的,给资源调试带来不可能检测的问题。这就是为什么必须真机调试的原因了。因为无法卸载。
- 但是InstanceId给我们在编辑器模式下卸载内存资源,编辑器模式调试资源带来可能。首先通过AssetDatabase.GetDependencies(path, true)获取加载prefab的所有加载的依赖资源,记录所有依赖资源的InstanceId,通过Resources.FindObjectsOfTypeAll();获取指定类型的内存中原始资源对象。循环遍历通过InstanceId匹配内存对象。然后获取内存对象后,通过Resources.UnloadAsset(Object)卸载内存。但是prefab是无法卸载的,prefab几乎不占内存,说白了prefab就是组织资源的一个配置文件。只要把相关依赖的基础资源卸载,prefab占的资源可以忽略。因为在下次加载prefab原始资源是加载返回的还是这个prefab内存资源,只不过重新加载依赖的资源。即使重新加载prefab这个配置文件也会影响多大。但是在资源中图集还是要单独处理。当然可以使用资源打包能完全做到内存卸载,但是开发阶段,不可能通过打包做资源测试。这个成本巨大,在开发阶段还是要通过
AssetDatabase.LoadAssetAtPath
或Resources.Load做资源加载。
- 后续有时间会写资源编辑器资源脚本。通过
AssetDatabase.LoadAssetAtPath
或Resources.Load做资源加载。使
在编辑器资源调试成为 可能,敬请期待吧。应该也是全网独份。因为截至现在,还没有人做到。即使官网Addressables也无法做到卸载,真是硬伤。。。