據我所知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”,第二次是將其保存到局部變量中。這並非最便捷的方法,但也夠用了,只需使用持久化的游戲對象設置下場景就好。
