Unity序列化和持久化
据我所知unity Serialization对于基本数据类型非常好用,但处理复类型时就有些基础缺陷了。
我的需求是连接组件和文件并永久保存这种关系。而组件(或游戏对象)的InstanceID不管用,因为每次场景加载都会不一样。
Unity内置的持久化策略是通过“m_LocalIdentfierInFile”(将检视面板视图切换为Debug模式即可看到该字段)来连接场景文件和游戏对象及其组件的(这个肯定是Unity的拼写错误,应该是Identifier,当然它也没有要改正的倾向)。
所以我也可以利用这个字段来做些什么啊。但最大的难题是UnityEngine没有提供任何可以使用该字段的接口,也没说明要如何用,几番查证得知这是在引擎的C++端实现的。
废话少说,多方尝试之后最后想出了一个简单的解决办法,就是在编辑器中复制一份“m_LocalIdentfierInFile”,并将其保存为序列化的属性。
但是,它对于运行时创建的游戏对象或组件并不管用。
下面是实现方法 :
- // This is a copy of the "m_localIndentiferInFile"
- // (don't forget to use [Serializable] on the class)
- [SerializeField]
- private int persistentID = -1;
结合下面这段代码就可以访问“m_LocalIdentfierInFile”了,确保该段代码添加了编辑器预定义宏(#ifdef UNITY_EDITOR),并且不要引用UnityEditor命名空间,否则会导致编译失败。
- // Init this instance, it's public so it can be called from a InspectorScript
- // is only set via the unity editor
- public void init ()
- {
- #if UNITY_EDITOR
- PropertyInfo inspectorModeInfo =
- typeof(UnityEditor.SerializedObject).GetProperty ("inspectorMode",
- BindingFlags.NonPublic | BindingFlags.Instance);
- UnityEditor.SerializedObject serializedObject =
- new UnityEditor.SerializedObject (comp);
- inspectorModeInfo.SetValue (serializedObject, UnityEditor.InspectorMode.Debug, null);
- UnityEditor.SerializedProperty localIdProp =
- serializedObject.FindProperty ("m_LocalIdentfierInFile")
- //Debug.Log ("found property: " + localIdProp.intValue);
- persistentID = localIdProp.intValue;
- //Important set the component to dirty so it won't be overriden from a prefab!
- UnityEditor.EditorUtility.SetDirty (this);
- #endif
- }
上面的脚本最初由thelackey3326发布在Unity论坛中,最后加上一句“UnityEditor.EditorUtility.SetDirty (this);”,以防预制件persistentID被重写。
下面是在OnEnable()中调用init()方法:
- public void OnEnable ()
- {
- myPersist.init ();
- }
这只在场景保存后有用(保存之前m_LocalIdentfierInFile == 0),在创建了游戏对象或拖拽预制件之后保存场景且至少点击一次游戏对象!
通常如果将预制件拖拽到场景中,就表示点击了它并进行了一些操作。
在场景即将保存时可以访问其中一些数据,Unity没有提供保存场景后进行访问的方法。下面的代码是在保存场景前调用init()函数:
- static string[] OnWillSaveAssets (string[] paths)
- {
- Object[] objs = Component.FindObjectsOfType (typeof(PersistObject));
- //Debug.Log ("OnWillSaveAssets " + objs.Length);
- foreach (Object obj in objs) {
- PersistObject persist = (PersistObject)obj;
- persist.init ();
- }
- return paths;
- }
完整源码点此链接。
以上代码表示,如果创建一个游戏对象,并使用编辑器获取“m_LocalIdentfierInFile”字段,就必须在此之后至少保存两次场景。第一次是让Unity设置“m_LocalIdentfierInFile”,第二次是将其保存到局部变量中。这并非最便捷的方法,但也够用了,只需使用持久化的游戏对象设置下场景就好。