Unity serialization and persistence

As far as I know Unity  Serialization works great for basic data types, but has some fundamental flaws when dealing with complex types.
 
My need is to connect components and files and keep this relationship permanently. The InstanceID of the component (or game object) doesn't work because it will be different each time the scene is loaded.
 
Unity's built-in persistence strategy is to connect scene files and game objects and their components through "m_LocalIdentfierInFile" (you can see this field by switching the inspector view to Debug mode) (this must be a spelling error of Unity, it should be Identifier , of course it has no tendency to correct it either).
 
So I can also use this field to do something. But the biggest problem is that UnityEngine does not provide any interface that can use this field, nor does it explain how to use it. After several verifications, it is found that this is implemented on the C++ side of the engine.
 
Without further ado, after many attempts, I finally came up with a simple solution, which is to copy a copy of "m_LocalIdentfierInFile" in the editor and save it as a serialized property.
 
However, it does not work for game objects or components created at runtime.
 
Here's how to do it :  
 
  1. // This is a copy of the "m_localIndentiferInFile"
  2. // (don't forget to use [Serializable] on the class)
  3. [SerializeField]
  4. private int persistentID = -1;
copy code
 
Combined with the following code, you can access "m_LocalIdentfierInFile", make sure that the editor predefined macro (#ifdef UNITY_EDITOR) is added to this code, and do not reference the UnityEditor namespace, otherwise the compilation will fail.

 

  1. // Init this instance, it's public so it can be called from a InspectorScript
  2. // is only set via the unity editor
  3. public void init ()
  4. {
  5.     #if UNITY_EDITOR
  6.     PropertyInfo inspectorModeInfo =
  7.     typeof(UnityEditor.SerializedObject).GetProperty ("inspectorMode",
  8.     BindingFlags.NonPublic | BindingFlags.Instance);
  9.     UnityEditor.SerializedObject serializedObject = 
  10.     new UnityEditor.SerializedObject (comp);
  11.     inspectorModeInfo.SetValue (serializedObject, UnityEditor.InspectorMode.Debug, null);
  12.     UnityEditor.SerializedProperty localIdProp =
  13.     serializedObject.FindProperty ("m_LocalIdentfierInFile")  
  14.     //Debug.Log ("found property: " + localIdProp.intValue);
  15.     persistentID = localIdProp.intValue;
  16.     //Important set the component to dirty so it won't be overriden from a prefab!
  17.     UnityEditor.EditorUtility.SetDirty (this);
  18.     #endif
  19. }
copy code
 
上面的脚本最初由thelackey3326发布在Unity论坛中,最后加上一句“UnityEditor.EditorUtility.SetDirty (this);”,以防预制件persistentID被重写。
 
下面是在OnEnable()中调用init()方法:
 
  1. public void OnEnable ()
  2. {
  3.     myPersist.init ();
  4. }
复制代码
 
这只在场景保存后有用(保存之前m_LocalIdentfierInFile == 0),在创建了游戏对象或拖拽预制件之后保存场景且至少点击一次游戏对象!
 
通常如果将预制件拖拽到场景中,就表示点击了它并进行了一些操作。
 
 
在场景即将保存时可以访问其中一些数据,Unity没有提供保存场景后进行访问的方法。下面的代码是在保存场景前调用init()函数:
 
  1. static string[] OnWillSaveAssets (string[] paths)
  2.     {
  3.         Object[] objs = Component.FindObjectsOfType (typeof(PersistObject));
  4.         //Debug.Log ("OnWillSaveAssets " + objs.Length);
  5.         foreach (Object obj in objs) {
  6.                 PersistObject persist = (PersistObject)obj;
  7.                 persist.init ();
  8.         }
  9.         return paths;
  10.     }
复制代码
 
完整源码点此链接
 
以上代码表示,如果创建一个游戏对象,并使用编辑器获取“m_LocalIdentfierInFile”字段,就必须在此之后至少保存两次场景。第一次是让Unity设置“m_LocalIdentfierInFile”,第二次是将其保存到局部变量中。这并非最便捷的方法,但也够用了,只需使用持久化的游戏对象设置下场景就好。
 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326848689&siteId=291194637