当点击Play以后,EditorWindow中的变量会被莫名其妙销毁.

来自:https://blog.csdn.net/bighead3827/article/details/79369901

最近在写Unity Editor的小工具,发现一些个人无法解释的问题.

当点击Play以后,EditorWindow中的变量如果引用是一个自定义类,则此变量会被莫名其妙销毁.

解决办法是在自定义类的名称前面加上一个一行代码,如下

[System.Serializable]
class CustomClass
{
}

这样引用此类的变量就不会被销毁了.


据说,Unity这么做是因为Play以后把界面中能保存的对象序列化以后保存到硬盘.

在Playing模式结束以后在从硬盘读取这些序列化的对象进行反序列化恢复之前的工作状态.

自定义的类如果不继承什么乱七八糟的类是不能自动序列化的,所以需要在前面加上限定告诉Unity这个对象要被序列化.

另:如果你的EditorWindow被关闭了,无论用任何方式都无法保存变量.必须被销毁.重新开启一切从零开始.

---------------------------------------------------------------------------------------------------------------

扫描二维码关注公众号,回复: 15182447 查看本文章

最新研究发现 点击Play重新编译以后会触发所有Editor的OnDisable然后在触发OnEnable

也就是说触发OnDisable以后发生了下列事情

1.所有Static成员都会重新调用赋值为默认值.

2.非Static成员,如果是值类型或者个别Unity对象不销毁,否则进入第3步

3检查是否有[System.Serializable]标识,没有标识就销毁这个成员否则不销毁并进入第4步

4.递归向内继续检查重复2 - 4步骤

-----------------------------------------------------------------------------------------------------------------

被折磨了2天以后还是老老实实的看官方文档吧.

在拓展Unity的时候,Unity编辑器会热重载你修改的脚本,这样就不需要每次重启编辑器让更改生效了.(这个我知道....)

但是在热重载之前,他会把你脚本中的可序列化变量进行临时存储.然后在加载脚本后将其回复.如果是不可以序列化的变量,那么在热重载以后将会丢失!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!(原来它他妈的是这么做的)

Unity会将Scenes,Assets和AssetBundles保存到硬盘中.也将我们可序列化的脚本保存到硬盘中.

Inspector 窗口修改的数据会直接序列化,不会和我们写的脚本交互,也不会通知我们修改?

序列化规则

具有Serializable特性的自定义非抽象非泛型类(继承到泛型类貌似也不行,并且这条不满足,是不是SerializeField字段都不会序列化..)

字段是公共的或者有[SerializeField]特性

不是静态的字段,不是const类型字段,不是只读的.

引用类型至少有一个可序列的字段?

具有Serializable特性的自定义结构

UnityEngine.Object的子类

原始数据类型 int,float,double,bool,string等等

EnumType枚举类型

Unity内置类型,Vector2,Vector3,Vector4,Rect,Quaternion,Matrix4x4,Color,Color32,LayerMask,AnimationCurve,Gradient,RectOffset,GUIStyle

看了这些应该明白了,像Dic这种字典,如果你动态增加了值,每次修改脚本时候都会被清空.因为Dic不能被序列化

Array和List可以被序列化,条件是存储的值必须是简单类型,也就是可以序列化的类型,如果Array中你存储的是若干个Dic字典或者嵌套List,肯定不会被序列化了.

如果非要想序列化,可以让把存储的类标记为Serializable,并实现 ISerializationCallbackReceiver接口来实现此功能.

经过测试发现如果你的类实例没有被可实例化的对象引用,也不会被序列化.比如Class的Static Instance只有自己持有自己引用的时候.

经过测试发现被序列化的实例,反序列化以后构造函数会被自动调用.无论你是否在外部new这个对象,都只被调用一次.由此猜测序列化之前和序列化之后的对象是不同的两个实例.

经过测试发现就算List<System.Object>存储的是int,也不会被序列化.必须用List<int>才可以被正确识别,序列化array和list是按照声明类型来判断值是否可以序列化.

经测试发现,一个List<FatherClass>存储了ChildClass,反序列化时候会被反成FatherClass,多态丢失了.

经测试发现FatherClass标记为Serializable而ChildClass没有标记,ChildClass c = new ChildClass() 则c也会被认为是不可序列化的.FatherClass F = new ChildClass()可以序列化

所以猜测 和List一样,序列化只检查声明时候的类型是否符合序列化要求,反序列化时候也只会反成声明的类型.

丢失多态是一个非常让人愤怒的事情.怎么办,老外们用继承ScriptableObject方式实现了反序列化的多态.这样就可以正确的反序列化了.这是一个比较常用的方法.

需要注意:通过CreateInstance<>来创建ScriptableObject对象,需要将hideFlags属性设置为HideFlags.HideAndDontSave.否则会在Play和Stop时候被GC掉.

GC原则不是太清楚,发现个问题,如果是有引用并且不设置HideAndDontSave,反序列化以后的引用的值为一个字符串"null",但是对象的属性都是存在的.设置HideAndDontSave之后解决.

想要GC需要手动调函数

DestroyImmediate( ScriptableObject );

这样还可以实现单例,Resources.FindObjectsofTypeAll<T>.FirstorDefault();来获取已经Create的对象.

至此,折磨至第五天解决了自己需求.
 

猜你喜欢

转载自blog.csdn.net/youmangu/article/details/125971597