【轉】Unity利用Xml和Json保存場景

原文:http://blog.csdn.net/y1196645376/article/details/52549082

1.首先你得有一個場景。這個迷宮地形我搭了好久。。


可以看到地圖中有個環境Env,人物Player,燈光Light,UGUI事件系統EventSystem,畫布Canvas。Init不用管,先刪掉。我們把這幾個物品全部做成Prefab。我放在了Prefab的文件夾裏面。

值得注意的是,如果這些Prefab或者其子物體中身上所掛腳本有pubulic變量添加的是其他物體或者組件的引用需要將這些變量全部私有化然後放到Awake函數裏面通過Game.FindWithTag()或者Transform.Find等函數動態獲取。

當然,以上的解決方案可能讓你很不習慣,沒有更好的解決方法了嗎?當然有,這個教程中我們只是對資源設置了Prefab。後面我們用篇文章來講述AssetBundle的使用,而這個技術不僅實現了資源的打包,而且很好的處理了這些資源依賴關係。這也就是後話了。。。這篇文章不涉及。


2.做完以上步驟之後,我們對資源的處理就完畢了,剩下我們就來編寫代碼實現對場景信息記錄。大概的思路就是:讀取所有場景信息,然後對於每個場景遍歷其所有的Prefab物體,然後記錄其座標,旋轉,大小。( 注意:下面代碼中涉及到LitJsond包的使用,請將LitJson.dll放到工程的Plugin目錄下 )

[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using UnityEditor;  
  4. using System.Collections.Generic;  
  5. using System.Xml;  
  6. using System.IO;  
  7. using System.Text;  
  8. using LitJson;  
  9. public class ScencePack : Editor  
  10. {  
  11.     //將所有遊戲場景導出爲XML格式  
  12.     [MenuItem("GameObject/ExportXML")]  
  13.     static void ExportXML()  
  14.     {  
  15.         string filepath = Application.dataPath + @"/StreamingAssets/my.xml";  
  16.         if (!File.Exists(filepath))  
  17.         {  
  18.             File.Delete(filepath);  
  19.         }  
  20.         XmlDocument xmlDoc = new XmlDocument();  
  21.         XmlElement root = xmlDoc.CreateElement("gameObjects");  
  22.         //遍歷所有的遊戲場景  
  23.         foreach (UnityEditor.EditorBuildSettingsScene S in UnityEditor.EditorBuildSettings.scenes)  
  24.         {  
  25.             //當關卡啓用  
  26.             if (S.enabled)  
  27.             {  
  28.                 //得到關卡的名稱  
  29.                 string name = S.path;  
  30.                 //打開這個關卡  
  31.                 EditorApplication.OpenScene(name);  
  32.                 XmlElement scenes = xmlDoc.CreateElement("scenes");  
  33.                 scenes.SetAttribute("name", name);  
  34.                 foreach (GameObject obj in Object.FindObjectsOfType(typeof(GameObject)))  
  35.                 {  
  36.                     if (obj.transform.parent == null)  
  37.                     {  
  38.                         XmlElement gameObject = xmlDoc.CreateElement("gameObjects");  
  39.                         gameObject.SetAttribute("name", obj.name);  
  40.   
  41.                         gameObject.SetAttribute("asset", obj.name + ".prefab");  
  42.                         XmlElement transform = xmlDoc.CreateElement("transform");  
  43.                         XmlElement position = xmlDoc.CreateElement("position");  
  44.                         XmlElement position_x = xmlDoc.CreateElement("x");  
  45.                         position_x.InnerText = obj.transform.position.x + "";  
  46.                         XmlElement position_y = xmlDoc.CreateElement("y");  
  47.                         position_y.InnerText = obj.transform.position.y + "";  
  48.                         XmlElement position_z = xmlDoc.CreateElement("z");  
  49.                         position_z.InnerText = obj.transform.position.z + "";  
  50.                         position.AppendChild(position_x);  
  51.                         position.AppendChild(position_y);  
  52.                         position.AppendChild(position_z);  
  53.   
  54.                         XmlElement rotation = xmlDoc.CreateElement("rotation");  
  55.                         XmlElement rotation_x = xmlDoc.CreateElement("x");  
  56.                         rotation_x.InnerText = obj.transform.rotation.eulerAngles.x + "";  
  57.                         XmlElement rotation_y = xmlDoc.CreateElement("y");  
  58.                         rotation_y.InnerText = obj.transform.rotation.eulerAngles.y + "";  
  59.                         XmlElement rotation_z = xmlDoc.CreateElement("z");  
  60.                         rotation_z.InnerText = obj.transform.rotation.eulerAngles.z + "";  
  61.                         rotation.AppendChild(rotation_x);  
  62.                         rotation.AppendChild(rotation_y);  
  63.                         rotation.AppendChild(rotation_z);  
  64.   
  65.                         XmlElement scale = xmlDoc.CreateElement("scale");  
  66.                         XmlElement scale_x = xmlDoc.CreateElement("x");  
  67.                         scale_x.InnerText = obj.transform.localScale.x + "";  
  68.                         XmlElement scale_y = xmlDoc.CreateElement("y");  
  69.                         scale_y.InnerText = obj.transform.localScale.y + "";  
  70.                         XmlElement scale_z = xmlDoc.CreateElement("z");  
  71.                         scale_z.InnerText = obj.transform.localScale.z + "";  
  72.   
  73.                         scale.AppendChild(scale_x);  
  74.                         scale.AppendChild(scale_y);  
  75.                         scale.AppendChild(scale_z);  
  76.   
  77.                         transform.AppendChild(position);  
  78.                         transform.AppendChild(rotation);  
  79.                         transform.AppendChild(scale);  
  80.   
  81.                         gameObject.AppendChild(transform);  
  82.                         scenes.AppendChild(gameObject);  
  83.                         root.AppendChild(scenes);  
  84.                         xmlDoc.AppendChild(root);  
  85.                         xmlDoc.Save(filepath);  
  86.   
  87.                     }  
  88.                 }  
  89.             }  
  90.         }  
  91.         //刷新Project視圖, 不然需要手動刷新哦  
  92.         AssetDatabase.Refresh();  
  93.     }  
  94.     //將所有遊戲場景導出爲JSON格式  
  95.     [MenuItem("GameObject/ExportJSON")]  
  96.     static void ExportJSON()  
  97.     {  
  98.         string filepath = Application.dataPath + @"/StreamingAssets/json.txt";  
  99.         FileInfo t = new FileInfo(filepath);  
  100.         if (!File.Exists(filepath))  
  101.         {  
  102.             File.Delete(filepath);  
  103.         }  
  104.         StreamWriter sw = t.CreateText();  
  105.   
  106.         StringBuilder sb = new StringBuilder();  
  107.         JsonWriter writer = new JsonWriter(sb);  
  108.         writer.WriteObjectStart();  
  109.         writer.WritePropertyName("GameObjects");  
  110.         writer.WriteArrayStart();  
  111.   
  112.         foreach (UnityEditor.EditorBuildSettingsScene S in UnityEditor.EditorBuildSettings.scenes)  
  113.         {  
  114.             if (S.enabled)  
  115.             {  
  116.                 string name = S.path;  
  117.                 EditorApplication.OpenScene(name);  
  118.                 writer.WriteObjectStart();  
  119.                 writer.WritePropertyName("scenes");  
  120.                 writer.WriteArrayStart();  
  121.                 writer.WriteObjectStart();  
  122.                 writer.WritePropertyName("name");  
  123.                 writer.Write(name);  
  124.                 writer.WritePropertyName("gameObject");  
  125.                 writer.WriteArrayStart();  
  126.   
  127.                 foreach (GameObject obj in Object.FindObjectsOfType(typeof(GameObject)))  
  128.                 {  
  129.                     if (obj.transform.parent == null)  
  130.                     {  
  131.                         writer.WriteObjectStart();  
  132.                         writer.WritePropertyName("name");  
  133.                         writer.Write(obj.name);  
  134.                         writer.WritePropertyName("asset");  
  135.                         writer.Write(obj.name + ".prefab");  
  136.                         writer.WritePropertyName("position");  
  137.                         writer.WriteObjectStart();  
  138.                         writer.WritePropertyName("x");  
  139.                         writer.Write(obj.transform.position.x.ToString("F5"));  
  140.                         writer.WritePropertyName("y");  
  141.                         writer.Write(obj.transform.position.y.ToString("F5"));  
  142.                         writer.WritePropertyName("z");  
  143.                         writer.Write(obj.transform.position.z.ToString("F5"));  
  144.                         writer.WriteObjectEnd();  
  145.   
  146.                         writer.WritePropertyName("rotation");  
  147.                         writer.WriteObjectStart();  
  148.                         writer.WritePropertyName("x");  
  149.                         writer.Write(obj.transform.rotation.eulerAngles.x.ToString("F5"));  
  150.                         writer.WritePropertyName("y");  
  151.                         writer.Write(obj.transform.rotation.eulerAngles.y.ToString("F5"));  
  152.                         writer.WritePropertyName("z");  
  153.                         writer.Write(obj.transform.rotation.eulerAngles.z.ToString("F5"));  
  154.                         writer.WriteObjectEnd();  
  155.   
  156.                         writer.WritePropertyName("scale");  
  157.                         writer.WriteObjectStart();  
  158.                         writer.WritePropertyName("x");  
  159.                         writer.Write(obj.transform.localScale.x.ToString("F5"));  
  160.                         writer.WritePropertyName("y");  
  161.                         writer.Write(obj.transform.localScale.y.ToString("F5"));  
  162.                         writer.WritePropertyName("z");  
  163.                         writer.Write(obj.transform.localScale.z.ToString("F5"));  
  164.                         writer.WriteObjectEnd();  
  165.   
  166.                         writer.WriteObjectEnd();  
  167.                     }  
  168.                 }  
  169.   
  170.                 writer.WriteArrayEnd();  
  171.                 writer.WriteObjectEnd();  
  172.                 writer.WriteArrayEnd();  
  173.                 writer.WriteObjectEnd();  
  174.             }  
  175.         }  
  176.         writer.WriteArrayEnd();  
  177.         writer.WriteObjectEnd();  
  178.   
  179.         sw.WriteLine(sb.ToString());  
  180.         sw.Close();  
  181.         sw.Dispose();  
  182.         AssetDatabase.Refresh();  
  183.     }  
  184. }  
( 申明: 以上代碼源自雨鬆Momo大神,本人根據工程實際情況可能有地方進行了略微修改 )

創建好腳本保存後,你會發現在Unity工具欄上GameObject那欄下拉列表中會出現 ExportXML 和 ExportJSON。保存場景。

然後在工程中創建一個文件夾命名StreamingAssets。然後點擊以上兩個選項其一。然後就會在該目錄中生成對應的記錄文件。


我們可以打開這兩個文件進行查看,記錄的就是工程的所有場景信息和每個場景中物體的Transform信息。


3.加載場景。大概思路:我們就可以通過解析這些文件,然後得到自身場景的Prefab的Transform信息,然後根據asset路徑信息去加載prefab然後實例到到場景中,再調整座標,大小,旋轉。即可復原原來的場景。

下面是加載場景的腳本:

[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using System.Xml;  
  4. using System.IO;  
  5. using UnityEditor;  
  6. using LitJson;  
  7. public class LoadScene: MonoBehaviour  
  8. {  
  9.   
  10.     [MenuItem("GameObject/ImprotXML")]  
  11.     public static void LoadSenceXML()  
  12.     {  
  13.   
  14.         //電腦和iphong上的路徑是不一樣的,這裏用標籤判斷一下。  
  15. #if UNITY_EDITOR  
  16.         string filepath = Application.dataPath + "/StreamingAssets" + "/my.xml";  
  17. #elif UNITY_IPHONE  
  18.       string filepath = Application.dataPath +"/Raw"+"/my.xml";  
  19. #endif  
  20.         //如果文件存在話開始解析。  
  21.         if (File.Exists(filepath))  
  22.         {  
  23.             XmlDocument xmlDoc = new XmlDocument();  
  24.             xmlDoc.Load(filepath);  
  25.             XmlNodeList nodeList = xmlDoc.SelectSingleNode("gameObjects").ChildNodes;  
  26.             foreach (XmlElement scene in nodeList)  
  27.             {  
  28.                 //因爲我的XML是把所有遊戲對象全部導出, 所以這裏判斷一下只解析需要的場景中的遊戲對象  
  29.                 //JSON和它的原理類似  
  30.                 string path = scene.GetAttribute("name");  
  31.                 string name = path.Substring(path.LastIndexOf('/')+1,path.LastIndexOf('.')-path.LastIndexOf('/')-1);  
  32.                 Debug.Log(name);  
  33.                 if (!name.Equals(Application.loadedLevelName))  
  34.                 {  
  35.                     continue;  
  36.                 }  
  37.   
  38.                 foreach (XmlElement gameObjects in scene.ChildNodes)  
  39.                 {  
  40.   
  41.                     string asset = "Assets/Prefab/" + gameObjects.GetAttribute("asset");  
  42.                     Vector3 pos = Vector3.zero;  
  43.                     Vector3 rot = Vector3.zero;  
  44.                     Vector3 sca = Vector3.zero;  
  45.                     foreach (XmlElement transform in gameObjects.ChildNodes)  
  46.                     {  
  47.                         foreach (XmlElement prs in transform.ChildNodes)  
  48.                         {  
  49.                             if (prs.Name == "position")  
  50.                             {  
  51.                                 foreach (XmlElement position in prs.ChildNodes)  
  52.                                 {  
  53.                                     switch (position.Name)  
  54.                                     {  
  55.                                         case "x":  
  56.                                             pos.x = float.Parse(position.InnerText);  
  57.                                             break;  
  58.                                         case "y":  
  59.                                             pos.y = float.Parse(position.InnerText);  
  60.                                             break;  
  61.                                         case "z":  
  62.                                             pos.z = float.Parse(position.InnerText);  
  63.                                             break;  
  64.                                     }  
  65.                                 }  
  66.                             }  
  67.                             else if (prs.Name == "rotation")  
  68.                             {  
  69.                                 foreach (XmlElement rotation in prs.ChildNodes)  
  70.                                 {  
  71.                                     switch (rotation.Name)  
  72.                                     {  
  73.                                         case "x":  
  74.                                             rot.x = float.Parse(rotation.InnerText);  
  75.                                             break;  
  76.                                         case "y":  
  77.                                             rot.y = float.Parse(rotation.InnerText);  
  78.                                             break;  
  79.                                         case "z":  
  80.                                             rot.z = float.Parse(rotation.InnerText);  
  81.                                             break;  
  82.                                     }  
  83.                                 }  
  84.                             }  
  85.                             else if (prs.Name == "scale")  
  86.                             {  
  87.                                 foreach (XmlElement scale in prs.ChildNodes)  
  88.                                 {  
  89.                                     switch (scale.Name)  
  90.                                     {  
  91.                                         case "x":  
  92.                                             sca.x = float.Parse(scale.InnerText);  
  93.                                             break;  
  94.                                         case "y":  
  95.                                             sca.y = float.Parse(scale.InnerText);  
  96.                                             break;  
  97.                                         case "z":  
  98.                                             sca.z = float.Parse(scale.InnerText);  
  99.                                             break;  
  100.                                     }  
  101.                                 }  
  102.                             }  
  103.                         }  
  104.   
  105.                         //拿到 旋轉 縮放 平移 以後克隆新遊戲對象  
  106.                         Debug.Log(asset);  
  107.                         Object obj = UnityEditor.AssetDatabase.LoadAssetAtPath(asset, typeof(GameObject));  
  108.                         GameObject ob = (GameObject)Instantiate(obj, pos, Quaternion.Euler(rot));  
  109.                         ob.transform.localScale = sca;  
  110.                         ob.name = obj.name;  
  111.                     }  
  112.                 }  
  113.             }  
  114.         }  
  115.     }  
  116.   
  117.     [MenuItem("GameObject/ImprotJSON")]  
  118.     public static void LoadSenceJSON()  
  119.     {  
  120. #if UNITY_EDITOR  
  121.         string filepath = Application.dataPath + "/StreamingAssets" + "/json.txt";  
  122. #elif UNITY_IPHONE  
  123.       string filepath = Application.dataPath +"/Raw"+"/json.txt";  
  124. #endif  
  125.   
  126.         StreamReader sr = File.OpenText(filepath);  
  127.         string strLine = sr.ReadToEnd();  
  128.         JsonData jd = JsonMapper.ToObject(strLine);  
  129.         JsonData gameObjectArray = jd["GameObjects"];  
  130.         int i, j, k;  
  131.         for (i = 0; i < gameObjectArray.Count; i++)  
  132.         {  
  133.             JsonData senseArray = gameObjectArray[i]["scenes"];  
  134.             for (j = 0; j < senseArray.Count; j++)  
  135.             {  
  136.                 string path = (string)senseArray[j]["name"];  
  137.                 string name = path.Substring(path.LastIndexOf('/') + 1, path.LastIndexOf('.') - path.LastIndexOf('/') - 1);  
  138.   
  139.   
  140.                 if (!name.Equals(Application.loadedLevelName))  
  141.                 {  
  142.                     continue;  
  143.                 }  
  144.                 JsonData gameObjects = senseArray[j]["gameObject"];  
  145.   
  146.                 for (k = 0; k < gameObjects.Count; k++)  
  147.                 {  
  148.                     string asset = "Assets/Prefab/" + (string)gameObjects[k]["asset"];  
  149.                     Vector3 pos = Vector3.zero;  
  150.                     Vector3 rot = Vector3.zero;  
  151.                     Vector3 sca = Vector3.zero;  
  152.   
  153.                     JsonData position = gameObjects[k]["position"];  
  154.                     JsonData rotation = gameObjects[k]["rotation"];  
  155.                     JsonData scale = gameObjects[k]["scale"];  
  156.   
  157.                     pos.x = float.Parse((string)position["x"]);  
  158.                     pos.y = float.Parse((string)position["y"]);  
  159.                     pos.z = float.Parse((string)position["z"]);  
  160.   
  161.                     rot.x = float.Parse((string)rotation["x"]);  
  162.                     rot.y = float.Parse((string)rotation["y"]);  
  163.                     rot.z = float.Parse((string)rotation["z"]);  
  164.   
  165.                     sca.x = float.Parse((string)scale["x"]);  
  166.                     sca.y = float.Parse((string)scale["y"]);  
  167.                     sca.z = float.Parse((string)scale["z"]);  
  168.   
  169.                     Debug.Log(asset);  
  170.                     Object obj = UnityEditor.AssetDatabase.LoadAssetAtPath(asset, typeof(GameObject));  
  171.                     GameObject ob = (GameObject)Instantiate(obj, pos, Quaternion.Euler(rot));  
  172.                     ob.transform.localScale = sca;  
  173.                     ob.name = obj.name;  
  174.   
  175.                 }  
  176.   
  177.             }  
  178.         }  
  179.   
  180.     }  
  181. }  
我們發現現在GameObject工具欄又多了兩個選項:ImportXML,ImportJSON。這就是導入場景信息。

有了以上代碼我們就可以對場景的復原了。

首先:我們先把場景裏面的物品全部刪了。( 注意:要先通過ExportXML or ExportJSON 導出場景記錄 )

然後:創建一個名爲Init的空物體。掛上一個腳本TestData. 然後只留Start函數然後執行 LoadScene.LoadSenceJSON() 或者 LoadScene.LoadSenceXML();


最後:運行遊戲。你會發現所有物體都被實例化出來了。



基本上xml和json的對場景保存和加載的方式如上。最後記錄信息文件也不是特別大:

不過你覺得你的工程物品實在有點多,信息文件太大,影響效率的話。可以使用二進制數據格式。主要就是使用BinaryWriter寫出二進制流,用BinaryRendeader讀入二進制流。這樣可以大大壓縮信息文件的大小。不過這種方式的壞處是解析的時候。必須要完全瞭解寫入信息的順序。也就是說如果是xml和json的話只需要知道屬性名就可以獲取了。而二進制沒有這些用於索引的字符,所以只能嚴格按照字節位數來控制每個變量的所佔位置。同時也必須嚴格按照字節對齊。不然一個變量範圍錯了,後面的跟着都錯了。



Demo工程地址:https://git.oschina.net/Wahh_1314/XmlAndJsonSceneDemo