其实关于这部分内容,雨松研究院已经写得很清楚了,也比较权威,链接在此: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更胜一筹。