zoukankan      html  css  js  c++  java
  • Unity中场景解析成Json和XML并还原场景

    其实关于这部分内容,雨松研究院已经写得很清楚了,也比较权威,链接在此:http://www.xuanyusong.com/archives/1919,但是现在还是想根据自己的思路整理一下

    其实原理就是讲Hierarchy中所有的父物体(即transform.parent == null)都做成预设,然后记录下每个父物体的Transform里的属性,记录到Xml或者Json文件中,然后再解析文件,把其中的预设加载到新场景里,就会得到与原场景一模一样的场景了

    首先在Project里新建一个Editor文件夹,里面新建一个脚本MyEditor.cs

    using UnityEngine;
    using System.Collections;
    using UnityEditor;
    using System.Collections.Generic;
    using System.Xml;
    using System.IO;
    using System.Text;
    using LitJson;

    public class MyEditor : Editor
    {

    //这样是修改了编译器,在菜单栏GameObject下面会多出ExportXML选项
    [MenuItem("GameObject/ExportXML")]
    static void ExportXML()
    {
    string filePath = Application.dataPath + "/StreamingAssets/my.xml";
    string prefabPath = Application.dataPath + "/Resources/Prefabs";

    if(!File.Exists(filePath))
    {
    File.Delete(filePath);
    }

    if(!Directory.Exists(prefabPath))
    {
    Directory.CreateDirectory(prefabPath);
    }

    XmlDocument xmlDoc = new XmlDocument ();
    XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration ("1.0", "utf-8", null);
    xmlDoc.AppendChild (xmlDec);
    XmlElement root = xmlDoc.CreateElement ("gameObjects");

    //遍历所有的游戏场景
    foreach(UnityEditor.EditorBuildSettingsScene s in UnityEditor.EditorBuildSettings.scenes)
    {
    //打开场景
    EditorApplication.OpenScene(s.path);
    XmlElement scenes = xmlDoc.CreateElement("scenes");
    scenes.SetAttribute("name", s.path);

    foreach(GameObject obj in Object.FindObjectsOfType(typeof(GameObject)))
    {
    //是否被禁用
    if(obj.transform.parent == null && obj.activeSelf)
    {
    //将该物体制作成预设并存放在Resources目录下的Prefabs中
    ExportPrefab(obj, prefabPath);

    XmlElement gameObject = xmlDoc.CreateElement("gameobjects");
    gameObject.SetAttribute("name", obj.name);
    gameObject.SetAttribute("asset", obj.name + ".prefab");

    XmlElement transform = xmlDoc.CreateElement("transform");

    XmlElement position = xmlDoc.CreateElement("position");
    XmlElement position_x = xmlDoc.CreateElement("x");
    XmlElement position_y = xmlDoc.CreateElement("y");
    XmlElement position_z = xmlDoc.CreateElement("z");
    position_x.InnerText = obj.transform.position.x + "";
    position_y.InnerText = obj.transform.position.y + "";
    position_z.InnerText = obj.transform.position.z + "";
    position.AppendChild(position_x);
    position.AppendChild(position_y);
    position.AppendChild(position_z);

    XmlElement rotation = xmlDoc.CreateElement("rotation");
    XmlElement rotation_x = xmlDoc.CreateElement("x");
    XmlElement rotation_y = xmlDoc.CreateElement("y");
    XmlElement rotation_z = xmlDoc.CreateElement("z");
    rotation_x.InnerText = obj.transform.rotation.eulerAngles.x + "";
    rotation_y.InnerText = obj.transform.rotation.eulerAngles.y + "";
    rotation_z.InnerText = obj.transform.rotation.eulerAngles.z + "";
    rotation.AppendChild(rotation_x);
    rotation.AppendChild(rotation_y);
    rotation.AppendChild(rotation_z);

    XmlElement scale = xmlDoc.CreateElement("scale");
    XmlElement scale_x = xmlDoc.CreateElement("x");
    XmlElement scale_y = xmlDoc.CreateElement("y");
    XmlElement scale_z = xmlDoc.CreateElement("z");
    scale_x.InnerText = obj.transform.localScale.x + "";
    scale_y.InnerText = obj.transform.localScale.y + "";
    scale_z.InnerText = obj.transform.localScale.z + "";
    scale.AppendChild(scale_x);
    scale.AppendChild(scale_y);
    scale.AppendChild(scale_z);

    transform.AppendChild(position);
    transform.AppendChild(rotation);
    transform.AppendChild(scale);

    gameObject.AppendChild(transform);
    scenes.AppendChild(gameObject);
    root.AppendChild(scenes);
    xmlDoc.AppendChild(root);
    xmlDoc.Save(filePath);
    }
    }
    }

    AssetDatabase.Refresh ();
    Debug.Log ("Xml export completed");
    Debug.Log (Time.realtimeSinceStartup);
    }

    [MenuItem("GameObject/ExportJson")]
    static void ExportJson()
    {
    string filePath = Application.dataPath + "/StreamingAssets/json.txt";
    string prefabPath = Application.dataPath + "/Resources/Prefabs";

    if(!File.Exists(filePath))
    {
    File.Delete(filePath);
    }

    if(!Directory.Exists(prefabPath))
    {
    Directory.CreateDirectory(prefabPath);
    }

    FileInfo t = new FileInfo (filePath);
    StreamWriter sw = t.CreateText ();

    StringBuilder sb = new StringBuilder ();
    JsonWriter writer = new JsonWriter (sb);
    writer.WriteObjectStart ();
    writer.WritePropertyName ("gameobjects");
    writer.WriteArrayStart ();

    //遍历所有的游戏场景
    foreach(UnityEditor.EditorBuildSettingsScene s in UnityEditor.EditorBuildSettings.scenes)
    {
    //打开场景
    EditorApplication.OpenScene(s.path);

    writer.WriteObjectStart();
    writer.WritePropertyName("scenes");
    writer.WriteArrayStart ();
    writer.WriteObjectStart();
    writer.WritePropertyName("name");
    writer.Write(s.path);
    writer.WritePropertyName("gameobject");
    writer.WriteArrayStart ();

    foreach(GameObject obj in Object.FindObjectsOfType(typeof(GameObject)))
    {
    if(obj.transform.parent == null && obj.activeSelf)
    {
    //将该物体制作成预设并存放在Resources目录下的Prefabs中
    ExportPrefab(obj, prefabPath);

    writer.WriteObjectStart();
    writer.WritePropertyName("name");
    writer.Write(obj.name);

    writer.WritePropertyName("position");
    writer.WriteArrayStart ();
    writer.WriteObjectStart();
    writer.WritePropertyName("x");
    writer.Write(obj.transform.position.x.ToString("F5"));//F5是格式转换
    writer.WritePropertyName("y");
    writer.Write(obj.transform.position.y.ToString("F5"));
    writer.WritePropertyName("z");
    writer.Write(obj.transform.position.z.ToString("F5"));
    writer.WriteObjectEnd();
    writer.WriteArrayEnd();

    writer.WritePropertyName("rotation");
    writer.WriteArrayStart ();
    writer.WriteObjectStart();
    writer.WritePropertyName("x");
    writer.Write(obj.transform.rotation.eulerAngles.x.ToString("F5"));
    writer.WritePropertyName("y");
    writer.Write(obj.transform.rotation.eulerAngles.y.ToString("F5"));
    writer.WritePropertyName("z");
    writer.Write(obj.transform.rotation.eulerAngles.z.ToString("F5"));
    writer.WriteObjectEnd();
    writer.WriteArrayEnd();

    writer.WritePropertyName("scale");
    writer.WriteArrayStart ();
    writer.WriteObjectStart();
    writer.WritePropertyName("x");
    writer.Write(obj.transform.localScale.x.ToString("F5"));
    writer.WritePropertyName("y");
    writer.Write(obj.transform.localScale.y.ToString("F5"));
    writer.WritePropertyName("z");
    writer.Write(obj.transform.localScale.z.ToString("F5"));
    writer.WriteObjectEnd();
    writer.WriteArrayEnd();

    writer.WriteObjectEnd();
    }
    }

    writer.WriteArrayEnd();
    writer.WriteObjectEnd();
    writer.WriteArrayEnd();
    writer.WriteObjectEnd();
    }

    writer.WriteArrayEnd ();
    writer.WriteObjectEnd();

    sw.WriteLine (sb.ToString());
    sw.Close ();
    sw.Dispose ();
    AssetDatabase.Refresh ();
    Debug.Log ("Json export completed");
    Debug.Log (Time.realtimeSinceStartup);
    }

    private static void ExportPrefab(GameObject gameObject, string prefabPath)
    {
    if(Directory.Exists(prefabPath))
    {
    PrefabUtility.CreatePrefab("Assets/Resources/Prefabs/" + gameObject.name + ".prefab", gameObject, ReplacePrefabOptions.ConnectToPrefab);
    }
    }
    }

    编译项目,并点击GameObject下的ExportXML和ExportJson选项,会发现在StreamingAssets多了json.txt和my.xml,同时多了文件夹Resources/Prefabs,文件夹里面的预设就是Hirearchy里的父物体的预设

    下面说说解析文件并还原场景

    这是XML的解析

    using UnityEngine;
    using System.Collections;
    using System.Xml;
    using System.IO;

    public class XML : MonoBehaviour
    {
    void Start ()
    {
    string filePath = Application.dataPath + "/StreamingAssets/my.xml";

    if(File.Exists(filePath))
    {
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.Load(filePath);
    XmlNodeList nodeList = xmlDoc.SelectSingleNode("gameObjects").ChildNodes;

    foreach(XmlElement scene in nodeList)
    {
    Debug.Log(scene.GetAttribute("name"));
    if(!scene.GetAttribute("name").Equals("Assets/Scenes/Demo.unity"))
    continue;

    foreach(XmlElement gameObjects in scene.ChildNodes)
    {
    string asset = "Prefabs/" + gameObjects.GetAttribute("name");

    Vector3 position = Vector3.zero;
    Vector3 rotation = Vector3.zero;
    Vector3 scale = Vector3.zero;

    foreach(XmlElement transform in gameObjects.ChildNodes)
    {
    foreach(XmlElement info in transform.ChildNodes)
    {
    if(info.Name.Equals("position"))
    {
    foreach(XmlElement pos in info.ChildNodes)
    {
    switch(pos.Name)
    {
    case "x":
    position.x = float.Parse(pos.InnerText);
    break;
    case "y":
    position.y = float.Parse(pos.InnerText);
    break;
    case "z":
    position.z = float.Parse(pos.InnerText);
    break;
    }
    }
    }
    else if(info.Name.Equals("rotation"))
    {
    foreach(XmlElement rot in info.ChildNodes)
    {
    switch(rot.Name)
    {
    case "x":
    rotation.x = float.Parse(rot.InnerText);
    break;
    case "y":
    rotation.y = float.Parse(rot.InnerText);
    break;
    case "z":
    rotation.z = float.Parse(rot.InnerText);
    break;
    }
    }
    }
    else if(info.Name.Equals("scale"))
    {
    foreach(XmlElement sca in info.ChildNodes)
    {
    switch(sca.Name)
    {
    case "x":
    scale.x = float.Parse(sca.InnerText);
    break;
    case "y":
    scale.y = float.Parse(sca.InnerText);
    break;
    case "z":
    scale.z = float.Parse(sca.InnerText);
    break;
    }
    }
    }
    }

    //1. 从Resource中读取相应的预设从而实例化出来一个对象
    GameObject obj = (GameObject)Instantiate(Resources.Load(asset), position, Quaternion.Euler(rotation));

    obj.transform.localScale = scale;
    }
    }
    }

    Debug.Log("Xml restore to scene");
    Debug.Log (Time.realtimeSinceStartup);
    }
    else
    {
    Debug.Log("File not exist");
    }
    }
    }

    这是Json的解析

    using UnityEngine;
    using System.Collections;
    using System.IO;
    using LitJson;

    public class Json : MonoBehaviour
    {
    void Start ()
    {
    string filePath = Application.dataPath + "/StreamingAssets/json.txt";
    StreamReader sr = File.OpenText (filePath);
    string strLine = sr.ReadToEnd ();
    JsonData jd = JsonMapper.ToObject (strLine);
    JsonData gameObjectArray = jd["gameobjects"];

    int i, j, k;
    //foreach(JsonData gameObject in gameObjectArray)
    for(i = 0; i < gameObjectArray.Count; i++)
    {
    JsonData sceneArray = gameObjectArray[i]["scenes"];

    //foreach(JsonData scene in sceneArray)
    for(j = 0; j < sceneArray.Count; j++)
    {
    string sceneName = sceneArray[j]["name"].ToString();

    if(!sceneName.Equals("Assets/Scenes/Demo.unity"))
    continue;

    JsonData gameObjects = sceneArray[j]["gameobject"];

    //foreach(JsonData obj in gameObjects)
    for(k = 0; k < gameObjects.Count; k++)
    {
    string objName = gameObjects[k]["name"].ToString();
    string asset = "Prefabs/" + objName;
    Vector3 position = Vector3.zero;
    Vector3 rotation = Vector3.zero;
    Vector3 scale = Vector3.zero;

    JsonData pos = gameObjects[k]["position"];
    JsonData rot = gameObjects[k]["rotation"];
    JsonData sca = gameObjects[k]["scale"];

    position.x = float.Parse((string)pos[0]["x"]);
    position.y = float.Parse((string)pos[0]["y"]);
    position.z = float.Parse((string)pos[0]["z"]);

    rotation.x = float.Parse((string)rot[0]["x"]);
    rotation.y = float.Parse((string)rot[0]["y"]);
    rotation.z = float.Parse((string)rot[0]["z"]);

    scale.x = float.Parse((string)sca[0]["x"]);
    scale.y = float.Parse((string)sca[0]["y"]);
    scale.z = float.Parse((string)sca[0]["z"]);

    GameObject ob = (GameObject)Instantiate(Resources.Load(asset), position, Quaternion.Euler(rotation));
    ob.transform.localScale = scale;
    }
    }
    }

    Debug.Log("Json restore to scene");
    Debug.Log (Time.realtimeSinceStartup);
    }
    }

    是不是挺简单呢,只要新建个场景,建个空物体把上述脚本挂在物体上,运行即可获得跟原来一模一样的场景啦~~

    PS:对于XML和Json对场景的解析和还原效率,我做了测试,下面是测试结果:

    当场景为小场景时,即物体个数较少(4个为例),draw call:110

     

    解析场景时间

    还原时间

    文件大小

    XML

    0.1372822

    0.1477898

    2k

    JSON

    0.1357037

    0.1179444

    1k

    当场景为大场景时,即物体个数比较多(100个为例),draw call:2848

     

    解析场景时间

    还原时间

    文件大小

    XML

    0.2660492

    0.1703795

    48k

    JSON

    0.1601105

    0.1329994

    22k

    当draw call比较大时,物体个数130,draw call:26068(此时cpu 45%,电脑已经比较卡了)

     

    解析场景时间

    还原时间

    文件大小

    XML

    0.3745174

    0.176897

    52k

    JSON

    0.124469

    0.136014

    24k

    个人觉得,还是Json更胜一筹。

  • 相关阅读:
    Java Scanner
    Java 继承
    什么叫异常?什么叫错误? 如何捕获异常? 如何抛出异常? 说说finally和final的区别! 什么是JDK?什么是JRE?说说它们之间的区别? 说说字符常量和字符串常量的区别
    数据分析三剑客之Pandas时间序列
    Css样式布局之Flex弹性盒子布局
    memcached的安装和使用
    Flask 第十八话之Restful API
    Flask 第十七话之信号
    Flask 第十六话之钩子函数
    Flask 第十五话之请求上下文及全局全局存储g对象
  • 原文地址:https://www.cnblogs.com/wanglufly/p/4449366.html
Copyright © 2011-2022 走看看