zoukankan      html  css  js  c++  java
  • 导出Unity场景为配置文件

    在处理很多人参与的项目时,很多时候在操作场景时,可能会牵扯到场景修改的冲突问题,这种时候,我们可以将场景以配置文件的形式存储下来(cocos的场景、android的view保存思想),可以采用json/XML/二进制等多种方式进行存储,这时,需要我们将Scenes中的所有GameObject以Prefab的形式存储,并且考虑到Prefab上的组件引用问题,就需要将原来直接拖入持有的方式获取的游戏对象,以Find/FindTag这类方法来初始化(可能面临的一个问题是:原来持有的是List等类型?这个问题暂时还没有想到很好的解决方式)。

    在存储时,主要使用了XML的方式来进行存储,对于每一个GameObject(Prefab),只需要存储其LocalPosition,LocalRotation,LocalScale,ParentPath等信息。主要的思想和设计主要就是上面提到的相关内容。下面直接上程序:

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

    //http://www.xuanyusong.com/archives/1919
    public class ExportScene : Editor
    {
    //将所有游戏场景导出为XML格式
    [MenuItem("GameObject/ExportScene2XML")]
    static void ExportScene2XML()
    {
    string tFilePath = Application.dataPath + @"/Resources/XML/Scenes.xml";
    if (!File.Exists(tFilePath))
    {
    File.Delete(tFilePath);
    }
    XmlDocument xmlDoc = new XmlDocument();
    XmlElement root = xmlDoc.CreateElement("AllGameObjects");

    IList<int> exportedGameObjectUniqueIDLst = new List<int>(); //加载过的prefab ID信息,防重复

    //遍历所有的游戏场景
    foreach (UnityEditor.EditorBuildSettingsScene S in UnityEditor.EditorBuildSettings.scenes)
    {
    if (S.enabled)
    {
    string tSceneName = S.path;
    EditorApplication.OpenScene(tSceneName);
    XmlElement tScenesRoot = xmlDoc.CreateElement("Scenes");
    tScenesRoot.SetAttribute("SceneName", tSceneName);
    List<GameObject> tLst = new List<GameObject>();
    tLst.Adds((GameObject[])Object.FindObjectsOfType(typeof(GameObject)));
    for(int i = tLst.Count - 1; i >= 0; i--)
    {
    GameObject obj = tLst[i]; //此处逻辑还未来得及优化
    if (obj == null)
    {
    continue;
    }
    GameObject tPrefab = PrefabUtility.FindRootGameObjectWithSameParentPrefab(obj); //当前物体对应的Prefab整体
    int tUniqueID = tPrefab.GetInstanceID();
    if (exportedGameObjectUniqueIDLst.Contains(tUniqueID))
    {
    continue;
    }
    exportedGameObjectUniqueIDLst.Add(tUniqueID);


    if (PrefabUtility.GetPrefabType(tPrefab) == PrefabType.None)
    {
    Debug.LogError("NonePrefab = " + tPrefab); //主动生成为Prefab(整体作为一个Prefab————>提示手动处理(存在Prefab和非Prefab的父子层次问题!!))
    //PrefabUtility.CreatePrefab(@"Assets/Resources/ScenePrefabs/" + tPrefab.name + @".prefab", tPrefab);
    }

    Stack<GameObject> tStack = getParentPrefabQueue(tPrefab);
    GameObject tObj = null;
    if (tStack.Count > 0)
    {
    tObj = tStack.Pop();
    }
    XmlElement tParent = tScenesRoot;
    while (tObj != null)
    {
    //先查找该节点是否存在
    XmlElement tElement = getXmlElementByID(xmlDoc, tObj.GetInstanceID().ToString());
    if (null == tElement)
    {
    tParent = generateNode(xmlDoc, ref tParent, tObj);
    }
    else
    {
    tParent = tElement;
    }
    if (tStack.Count > 0)
    {
    tObj = tStack.Pop();
    }
    else
    {
    tObj = null;
    }
    }
    //XmlElement tChild = generateNode(xmlDoc, ref tScenesRoot, tPrefab);
    root.AppendChild(tScenesRoot);
    xmlDoc.AppendChild(root);
    xmlDoc.Save(tFilePath);
    }
    }
    }
    //刷新Project视图
    AssetDatabase.Refresh();
    }

    //子对象的xml结点放在父对象下面
    private static XmlElement generateNode(XmlDocument _xmlDoc, ref XmlElement _parent, GameObject _prefab)
    {
    XmlElement tGameObject = _xmlDoc.CreateElement("GameObject");
    tGameObject.SetAttribute("ID", _prefab.GetInstanceID().ToString());
    tGameObject.SetAttribute("Name", _prefab.name);
    //tGameObject.SetAttribute("Asset", _prefab.name + ".prefab");
    string tRootPath = getRootPath(_prefab);
    if (!tRootPath.Equals(""))
    {
    tGameObject.SetAttribute("RootPath", tRootPath);
    }

    #if true
    Vector3 tPos = _prefab.transform.localPosition;
    if (!Mathf.Approximately(tPos.x, 0)) //位置为0作为默认值,不必存储
    {
    tGameObject.SetAttribute("Px", tPos.x.ToString("0.###"));
    }
    if (!Mathf.Approximately(tPos.y, 0))
    {
    tGameObject.SetAttribute("Py", tPos.y.ToString("0.###"));
    }
    if (!Mathf.Approximately(tPos.z, 0))
    {
    tGameObject.SetAttribute("Pz", tPos.z.ToString("0.###"));
    }
    #endif

    #if true
    Vector3 tAngle = _prefab.transform.localRotation.eulerAngles;
    if (!Mathf.Approximately(tAngle.x, 0)) //角度为0作为默认值,不必存储
    {
    tGameObject.SetAttribute("Rx", tAngle.x.ToString("0.###"));
    }
    if (!Mathf.Approximately(tAngle.y, 0))
    {
    tGameObject.SetAttribute("Ry", tAngle.y.ToString("0.###"));
    }
    if (!Mathf.Approximately(tAngle.z, 0))
    {
    tGameObject.SetAttribute("Rz", tAngle.z.ToString("0.###"));
    }
    #endif

    #if true
    Vector3 tScale = _prefab.transform.localScale;
    if (!Mathf.Approximately(tScale.x, 1)) //Scale为1作为默认值,不必存储
    {
    tGameObject.SetAttribute("Sx", tScale.x.ToString("0.###"));
    }
    if (!Mathf.Approximately(tScale.y, 1))
    {
    tGameObject.SetAttribute("Sy", tScale.y.ToString("0.###"));
    }
    if (!Mathf.Approximately(tScale.z, 1))
    {
    tGameObject.SetAttribute("Sz", tScale.z.ToString("0.###"));
    }
    #endif
    _parent.AppendChild(tGameObject);
    return tGameObject;
    }

    //根据ID获取结点
    private static XmlElement getXmlElementByID(XmlDocument _xmlDoc, string _id)
    {
    XmlNode tRoot = _xmlDoc.SelectSingleNode("AllGameObjects");
    if (null != tRoot)
    {
    XmlNodeList nodeList = tRoot.ChildNodes;
    XmlNode scene = null;
    for (int i = 0; i < nodeList.Count; i++)
    {
    scene = nodeList[i];
    XmlNode gameObject = null;
    for (int j = 0; j < scene.ChildNodes.Count; j++)
    {
    gameObject = scene.ChildNodes[j];
    XmlElement tRet = getXmlElementByID(_xmlDoc, gameObject, _id);
    if (tRet != null)
    {
    return tRet;
    }
    }
    }
    }
    return null;
    }

    private static XmlElement getXmlElementByID(XmlDocument _xmlDoc, XmlNode _gameObject, string _id)
    {
    XmlElement tRet = null;
    if (((XmlElement)_gameObject).GetAttribute("ID").Equals(_id)) //判断当前结点
    {
    tRet = (XmlElement)_gameObject;
    }
    else if (_gameObject.ChildNodes.Count > 0)     //递归查找子节点
    {
    XmlNode gameObject = null;
    for (int i = 0; i < _gameObject.ChildNodes.Count; i++)
    {
    gameObject = _gameObject.ChildNodes[i];
    tRet = getXmlElementByID(_xmlDoc, gameObject, _id);
    if (tRet != null)
    {
    break;
    }
    }
    }
    return tRet;
    }

    private static string getRootPath(GameObject _obj)
    {
    string tResult = "";
    Transform tParent = _obj.transform.parent;
    while (tParent != null)
    {
    if (tResult.Equals(""))
    {
    tResult = tParent.name;
    }
    else
    {
    tResult = tParent.name + "/" + tResult;
    }
    tParent = tParent.parent;
    }
    return tResult;
    }

    //先根据ID查找,结点是否存在
    private static Stack<GameObject> getParentPrefabQueue(GameObject _obj)
    {
    IList<int> tUniqueIDLst = new List<int>();
    Stack<GameObject> tStack = new Stack<GameObject>();
    tStack.Push(_obj);
    tUniqueIDLst.Add(_obj.GetInstanceID());
    Transform tParent = _obj.transform.parent;
    GameObject tPrefab = null;
    while (tParent != null)
    {
    tPrefab = PrefabUtility.FindRootGameObjectWithSameParentPrefab(tParent.gameObject);
    int tID = tPrefab.GetInstanceID();
    if (!tUniqueIDLst.Contains(tID))
    {
    tStack.Push(tPrefab);
    tUniqueIDLst.Add(tID);
    }
    tParent = tParent.parent;
    }
    return tStack;
    }


    }

    根据生成的XML配置文件和Resources下的Prefab信息,生成场景的逻辑如下:

    public class GenerateSceneFromXml
    {
    #region
    private static readonly object lockHelper = new object();
    private GenerateSceneFromXml()
    {
    }
    private static GenerateSceneFromXml instance = null;
    public static GenerateSceneFromXml Instance
    {
    private set
    {
    }
    get
    {
    if (null == instance)
    {
    lock (lockHelper)
    {
    if (null == instance)
    {
    instance = new GenerateSceneFromXml();
    }
    }
    }
    return instance;
    }
    }
    #endregion singleton

    public void Init()
    {
    TextAsset tAsset = Resources.Load<TextAsset>("XML/Scenes");
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(tAsset.text);

    XmlNodeList nodeList = xmlDoc.SelectSingleNode("AllGameObjects").ChildNodes;
    XmlNode scene = null;
    for(int i = 0; i < nodeList.Count; i++)
    {
    scene = nodeList[i];
    XmlNode gameObject = null;
    for (int j = 0; j < scene.ChildNodes.Count; j++) //遍历场景下的所有游戏对象
    {
    gameObject = scene.ChildNodes[j];
    generateRecursivily(gameObject);
    }
    }
    Resources.UnloadAsset(tAsset);
    }

    //生成自身,并依次递归生成子物体
    private GameObject generateRecursivily(XmlNode _node, Transform _parent = null)
    {
    Vector3 tPos = Vector3.zero;
    Vector3 tRot = Vector3.zero;
    Vector3 tScale = Vector3.one;

    XmlElement tElem = ((XmlElement)_node);
    tPos.x = float.Parse(tElem.GetAttributeValue("Px", "0"));
    tPos.y = float.Parse(tElem.GetAttributeValue("Py", "0"));
    tPos.z = float.Parse(tElem.GetAttributeValue("Pz", "0"));

    tRot.x = float.Parse(tElem.GetAttributeValue("Rx", "0"));
    tRot.y = float.Parse(tElem.GetAttributeValue("Ry", "0"));
    tRot.z = float.Parse(tElem.GetAttributeValue("Rz", "0"));

    tScale.x = float.Parse(tElem.GetAttributeValue("Sx", "1"));
    tScale.y = float.Parse(tElem.GetAttributeValue("Sy", "1"));
    tScale.z = float.Parse(tElem.GetAttributeValue("Sz", "1"));

    string tAsset = "ScenePrefabs/" + tElem.GetAttribute("Name");
    GameObject tObj = (GameObject)GameObject.Instantiate(Resources.Load(tAsset));
    //tObj.transform.parent = _parent;
    if (tElem.GetAttributeValue("RootPath", "").Equals(""))
    {
    tObj.transform.parent = null;
    }
    else
    {
    tObj.transform.parent = GameObject.Find(tElem.GetAttribute("RootPath")).transform;
    }
    tObj.transform.localPosition = tPos;
    tObj.transform.localEulerAngles = tRot;
    tObj.transform.localScale = tScale;
    tObj.name = tElem.GetAttribute("Name"); //去掉"(Clone)"

    if (_node.HasChildNodes)
    {
    for(int i = _node.ChildNodes.Count - 1; i >= 0; i--)
    {
    generateRecursivily(_node.ChildNodes[i], tObj.transform); 
    }
    }
    return tObj;
    }
    }

    当然,XML的存储效率没那么高,可以调整为json/二进制的存储方式。

  • 相关阅读:
    正交相机下实现滚轮按钮拖动,滚动滚轮缩放的功能
    C#标记 [已弃用] 的方法
    JDBC-Mybatis-Hibernate
    Data Structures and Algorithm Analysis in Java
    Maven
    Java面试
    Bootstrap
    CSS3
    HTML5
    JSON
  • 原文地址:https://www.cnblogs.com/wayland/p/4769459.html
Copyright © 2011-2022 走看看