Unity3D 場(chǎng)景導(dǎo)出成 XML 并解析還原場(chǎng)景

為了盡可能加快從網(wǎng)絡(luò)加載場(chǎng)景,我們通??梢园褕?chǎng)景先導(dǎo)出成 XML,把優(yōu)先級(jí)高的資源優(yōu)先加載并顯示(地形等),把可以進(jìn)入場(chǎng)景之后再加載的對(duì)象放到最后(比如場(chǎng)景里面的怪物等),本篇一部分代碼引用自:http://www.xuanyusong.com/archives/1919,導(dǎo)出場(chǎng)景部分在原作者的代碼基礎(chǔ)進(jìn)行了優(yōu)化,并且整理成了更加方便,容易使用的類庫(kù)。
先來(lái)搭建測(cè)試場(chǎng)景(測(cè)試場(chǎng)景來(lái)源網(wǎng)絡(luò)),并整理場(chǎng)景中的對(duì)象,如圖:
然后把場(chǎng)景中的對(duì)象都設(shè)置成預(yù)設(shè),方便打包成 assetbundle 文件(如何打包預(yù)設(shè)請(qǐng)查看),如圖:
接著我們編寫把場(chǎng)景打包成 XML 的代碼,取名 ExportSceneToXml.cs,大家可以先看這篇文章(http://www.xuanyusong.com/archives/1919),我在此基礎(chǔ)上面進(jìn)行了優(yōu)化,全部代碼如下:
</font>using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Text;
public class ExportSceneToXml : Editor
{
[MenuItem("Assets/Export Scene To XML From Selection")]
static void ExportXML()
{
string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "xml");
if (path.Length != 0)
{
Object[] selectedAssetList = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);
//遍歷所有的游戲?qū)ο?br /> foreach (Object selectObject in selectedAssetList)
{
// 場(chǎng)景名稱
string sceneName = selectObject.name;
// 場(chǎng)景路徑
string scenePath = AssetDatabase.GetAssetPath(selectObject);
// 場(chǎng)景文件
//string xmlPath = path; //Application.dataPath + "/AssetBundles/Prefab/Scenes/" + sceneName + ".xml";
// 如果存在場(chǎng)景文件,刪除
if(File.Exists(path)) File.Delete(path);
// 打開這個(gè)關(guān)卡
EditorApplication.OpenScene(scenePath);
XmlDocument xmlDocument = new XmlDocument();
// 創(chuàng)建XML屬性
XmlDeclaration xmlDeclaration = xmlDocument.CreateXmlDeclaration("1.0", "utf-8", null);
xmlDocument.AppendChild(xmlDeclaration);
// 創(chuàng)建XML根標(biāo)志
XmlElement rootXmlElement = xmlDocument.CreateElement("root");
// 創(chuàng)建場(chǎng)景標(biāo)志
XmlElement sceneXmlElement = xmlDocument.CreateElement("scene");
sceneXmlElement.SetAttribute("sceneName", sceneName);
foreach (GameObject sceneObject in Object.FindObjectsOfType(typeof(GameObject)))
{
// 如果對(duì)象是激活狀態(tài)
if (sceneObject.transform.parent == null && sceneObject.activeSelf)
{
// 判斷是否是預(yù)設(shè)
if(PrefabUtility.GetPrefabType(sceneObject) == PrefabType.PrefabInstance)
{
// 獲取引用預(yù)設(shè)對(duì)象
Object prefabObject = EditorUtility.GetPrefabParent(sceneObject);
if(prefabObject != null)
{
XmlElement gameObjectXmlElement = xmlDocument.CreateElement("gameObject");
gameObjectXmlElement.SetAttribute("objectName", sceneObject.name);
gameObjectXmlElement.SetAttribute("objectAsset", prefabObject.name);
XmlElement transformXmlElement = xmlDocument.CreateElement("transform");
// 位置信息
XmlElement positionXmlElement = xmlDocument.CreateElement("position");
positionXmlElement.SetAttribute("x", sceneObject.transform.position.x.ToString());
positionXmlElement.SetAttribute("y", sceneObject.transform.position.y.ToString());
positionXmlElement.SetAttribute("z", sceneObject.transform.position.z.ToString());
// 旋轉(zhuǎn)信息
XmlElement rotationXmlElement = xmlDocument.CreateElement("rotation");
rotationXmlElement.SetAttribute("x", sceneObject.transform.rotation.eulerAngles.x.ToString());
rotationXmlElement.SetAttribute("y", sceneObject.transform.rotation.eulerAngles.y.ToString());
rotationXmlElement.SetAttribute("z", sceneObject.transform.rotation.eulerAngles.z.ToString());
// 縮放信息
XmlElement scaleXmlElement = xmlDocument.CreateElement("scale");
scaleXmlElement.SetAttribute("x", sceneObject.transform.localScale.x.ToString());
scaleXmlElement.SetAttribute("y", sceneObject.transform.localScale.y.ToString());
scaleXmlElement.SetAttribute("z", sceneObject.transform.localScale.z.ToString());
transformXmlElement.AppendChild(positionXmlElement);
transformXmlElement.AppendChild(rotationXmlElement);
transformXmlElement.AppendChild(scaleXmlElement);
gameObjectXmlElement.AppendChild(transformXmlElement);
sceneXmlElement.AppendChild(gameObjectXmlElement);
}
}
}
}
rootXmlElement.AppendChild(sceneXmlElement);
xmlDocument.AppendChild(rootXmlElement);
// 保存場(chǎng)景數(shù)據(jù)
xmlDocument.Save(path);
// 刷新Project視圖
AssetDatabase.Refresh();
}
}
}
}
然后我們選中需要打包的場(chǎng)景,選擇把場(chǎng)景打包成 XML 的選項(xiàng),如圖:
生成完成,我們可以查看生成出的 XML 內(nèi)容,如圖:
這兒為什么說(shuō)是對(duì)原作者的代碼進(jìn)行了優(yōu)化,下面我們可以把場(chǎng)景中的一個(gè)對(duì)象名稱改成與預(yù)設(shè)名稱不同,如圖:
然后再次導(dǎo)出成 XML 文件,查看 XML 生成的內(nèi)容我們可以發(fā)現(xiàn),我們可以正確找到預(yù)設(shè)的名稱,如圖:
另外,我們還可以選擇場(chǎng)景中的哪些文件不用導(dǎo)出,方法很簡(jiǎn)單,我們可以先把場(chǎng)景中的對(duì)象禁用,再導(dǎo)出,如圖:
再次查看新導(dǎo)出的 XML 文件,我們會(huì)發(fā)現(xiàn) XML 中已經(jīng)不包括了被禁用對(duì)象的配置信息,如圖:
以上兩點(diǎn)是對(duì)原作者代碼的優(yōu)化,而且我也改成了使用右鍵導(dǎo)出,個(gè)人感覺(jué)這樣更加方便、實(shí)用。
現(xiàn)在回到場(chǎng)景中,我們可以把場(chǎng)景里面的對(duì)象全部刪除,因?yàn)閳?chǎng)景中已經(jīng)不需要這些對(duì)象了,我們需要通過(guò)代碼創(chuàng)建這些對(duì)象,如圖:
下面我們來(lái)看如何還原場(chǎng)景,有了 XML,我們解析 XML 就可以了,資源的加載可以看這篇文章(查看詳情),加載場(chǎng)景以及預(yù)設(shè)資源(assetbundle)的代碼如下:
using UnityEngine;
using System.Collections.Generic;
public class LoaderScene : MonoBehaviour
{
public UISlider progressBar;
public UILabel lblStatus;
private string scenePath;
void Awake()
{
string prefabPath = "file:///" + Application.dataPath + "/Assets/{0}.assetbundle";
this.scenePath = "file:///" + Application.dataPath + "/Assets/MainScene.unity3d";
IList<WwwLoaderPath> pathList = new List<WwwLoaderPath> ();
pathList.Add (new WwwLoaderPath (this.scenePath, Random.Range (0, 100), WwwLoaderTypeEnum.UNITY_3D));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "Lights"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "Particles"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "PhysicsCube"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "Player"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "Stamps"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "Statics"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "Terrain"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
pathList.Add (new WwwLoaderPath (string.Format(prefabPath, "Trees"), Random.Range (0, 100), WwwLoaderTypeEnum.ASSET_BUNDLE));
this.lblStatus.text = "場(chǎng)景加載中,請(qǐng)稍候。。。";
WwwLoaderManager.instance.Loader (pathList, onLoaderProgress, onLoaderComplete, "MainScene");
}
private void onLoaderProgress(string path, float currentValue, float totalValue)
{
this.progressBar.value = currentValue;
}
private void onLoaderComplete()
{
this.lblStatus.text = "場(chǎng)景正在初始化,請(qǐng)等待。。。";
Application.LoadLevelAsync("MainScene");
}
}
然后新建立一個(gè) C# 文件,取名:InitObject.cs,代碼如下:
using UnityEngine;
using System.Collections;
using System.Xml;
public class InitObject : MonoBehaviour
{
void Awake()
{
string xmlPath = Application.dataPath + "/Assets/MainScene.xml";
string prefabPath = "file:///" + Application.dataPath + "/Assets/{0}.assetbundle";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load (xmlPath);
// 使用 XPATH 獲取所有 gameObject 節(jié)點(diǎn)
XmlNodeList xmlNodeList = xmlDocument.SelectNodes("http://gameObject");
foreach(XmlNode xmlNode in xmlNodeList)
{
string gameObjectName = xmlNode.Attributes["objectName"].Value;
string prefabName = xmlNode.Attributes["objectAsset"].Value;
AssetBundle assetBundle = WwwDataManager.instance.GetDataAssetBundle(string.Format(prefabPath, prefabName));
if(assetBundle != null)
{
GameObject assetObject = (GameObject)assetBundle.Load(prefabName, typeof(GameObject));
if(assetObject != null)
{
GameObject gameObject = (GameObject)Instantiate(assetObject);
// 使用 XPATH 獲取 位置、旋轉(zhuǎn)、縮放數(shù)據(jù)
XmlNode positionXmlNode = xmlNode.SelectSingleNode("descendant::position");
XmlNode rotationXmlNode = xmlNode.SelectSingleNode("descendant::rotation");
XmlNode scaleXmlNode = xmlNode.SelectSingleNode("descendant::scale");
if(positionXmlNode != null && rotationXmlNode != null && scaleXmlNode != null)
{
gameObject.transform.position = new Vector3(float.Parse(positionXmlNode.Attributes["x"].Value), float.Parse(positionXmlNode.Attributes["y"].Value), float.Parse(positionXmlNode.Attributes["z"].Value));
gameObject.transform.rotation = Quaternion.Euler(new Vector3(float.Parse(rotationXmlNode.Attributes["x"].Value), float.Parse(rotationXmlNode.Attributes["y"].Value), float.Parse(rotationXmlNode.Attributes["z"].Value)));
gameObject.transform.localScale = new Vector3(float.Parse(scaleXmlNode.Attributes["x"].Value), float.Parse(scaleXmlNode.Attributes["y"].Value), float.Parse(scaleXmlNode.Attributes["z"].Value));
}
}
// 卸載引用的加載資源,釋放內(nèi)存
assetBundle.Unload(false);
}
}
xmlDocument = null;
}
}
然后我們?cè)诳盏膱?chǎng)景中新建立一個(gè)空對(duì)象,并且把代碼掛載到這個(gè)空對(duì)象上面,如圖:
再然后我們把這個(gè)場(chǎng)景打包成 .unity3d 文件,方便從網(wǎng)絡(luò)上面加載(詳情可以查看這篇文章),這樣所有的準(zhǔn)備工作都已經(jīng)做好了,全部的配置文件以及資源文件如下:
我們從加載場(chǎng)景運(yùn)行項(xiàng)目,我們可以先看到依次在加載主場(chǎng)景資源,加載完成之后進(jìn)入主場(chǎng)景,根據(jù) XML 的內(nèi)容,原場(chǎng)景被還原了回來(lái),如圖:
百度網(wǎng)盤下載地址:http://pan.baidu.com/s/1sj0RkHv 密碼: vbjd
相關(guān)文章
Unity3D 使用 WWW 加載場(chǎng)景并顯示進(jìn)度條
這篇文章主要介紹了Unity3D 使用 WWW 加載場(chǎng)景并顯示進(jìn)度條,并附上相關(guān)代碼,有需要的朋友參考下2014-10-20Unity3D游戲開發(fā) 宣雨松著 PDF掃描版[27MB]
《Unity3D游戲開發(fā)》通過(guò)實(shí)例詳細(xì)介紹了如何使用Unity 進(jìn)行游戲開發(fā),書中先簡(jiǎn)要介紹了Unity 環(huán)境搭建、編輯器和GUI 游戲界面相關(guān)的知識(shí),接著介紹了如何使用C# 和JavaScri2014-05-10Unity3D中自動(dòng)調(diào)用的方法總結(jié)
這篇文章主要介紹了Unity3D中自動(dòng)調(diào)用的方法總結(jié),需要的朋友可以參考下2014-04-24- 在本場(chǎng)景用到的拖尾效果可以查看我的另一篇文章,里面有詳細(xì)的介紹,刀光效果來(lái)自 Unity3D Assets 商店,只是把原作者的例子代碼整理了一下,變得非常簡(jiǎn)單實(shí)用的類。2014-10-20
Unity3D 實(shí)現(xiàn)怪物巡邏、按路線行走操作
這篇文章主要介紹了Unity3D 實(shí)現(xiàn)怪物巡邏、按路線行走操作,由于之前沒(méi)什么經(jīng)驗(yàn),就只能按照自己的想法很愚笨的實(shí)現(xiàn)的,也算拋磚引玉,如果讀者知道如何更簡(jiǎn)單的實(shí)現(xiàn)方式,2014-10-20