参考:http://www.xuanyusong.com/archives/1919
http://www.omuying.com/article/48.aspx
主要功能:
1.导出场景的配置文件
2.导出当前场景中资源的AssetBundle
3.客户端从服务器获取配置文件
4.解析配置文件,并根据配置文件下载AssetBundle
5.实例化并还原场景
1.场景设置:将需要导出的场景资源设置为预设
2.将场景配置导出为XML文件
[code]csharpcode:
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); //遍历所有的游戏对象 foreach (Object selectObject in selectedAssetList) { // 场景名称 string sceneName = selectObject.name; // 场景路径 string scenePath = AssetDatabase.GetAssetPath(selectObject); // 场景文件 //string xmlPath = path; //Application.dataPath + "/AssetBundles/Prefab/Scenes/" + sceneName + ".xml"; // 如果存在场景文件,删除 if(File.Exists(path)) File.Delete(path); // 打开这个关卡 EditorApplication.OpenScene(scenePath); XmlDocument xmlDocument = new XmlDocument(); // 创建XML属性 XmlDeclaration xmlDeclaration = xmlDocument.CreateXmlDeclaration("1.0", "utf-8", null); xmlDocument.AppendChild(xmlDeclaration); // 创建XML根标志 XmlElement rootXmlElement = xmlDocument.CreateElement("root"); // 创建场景标志 XmlElement sceneXmlElement = xmlDocument.CreateElement("scene"); sceneXmlElement.SetAttribute("sceneName", sceneName); foreach (GameObject sceneObject in Object.FindObjectsOfType(typeof(GameObject))) { // 如果对象是激活状态 if (sceneObject.transform.parent == null && sceneObject.activeSelf) { // 判断是否是预设 if(PrefabUtility.GetPrefabType(sceneObject) == PrefabType.PrefabInstance) { // 获取引用预设对象 Object prefabObject = EditorUtility.GetPrefabParent(sceneObject); if(prefabObject != null) { XmlElement gameObjectXmlElement = xmlDocument.CreateElement("gameObject"); gameObjectXmlElement.SetAttribute("objectName", sceneObject.name); gameObjectXmlElement.SetAttribute("objectAssetURL", 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()); // 旋转信息 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); // 保存场景数据 xmlDocument.Save(path); // 刷新Project视图 AssetDatabase.Refresh(); } } } }
导出结果参考:
[code]xmlcode:
<?xml version="1.0" encoding="utf-8"?> <root> <scene sceneName="DongTaiJiaZaiTest"> <gameObject objectName="Box" objectAssetURL="Box"> <transform> <position x="-1.293883" y="-0.07" z="1.41" /> <rotation x="270" y="0" z="0" /> <scale x="1" y="1" z="1" /> </transform> </gameObject> <gameObject objectName="meinv" objectAssetURL="meinv"> <transform> <position x="-1.75" y="1.36" z="11.28" /> <rotation x="0" y="97.43578" z="0" /> <scale x="1.09" y="1.09" z="1.09" /> </transform> </gameObject> <gameObject objectName="Envirment" objectAssetURL="Envirment"> <transform> <position x="0" y="0" z="0" /> <rotation x="0" y="0" z="0" /> <scale x="1" y="1" z="1" /> </transform> </gameObject> <gameObject objectName="RigidBodyFPSController" objectAssetURL="RigidBodyFPSController"> <transform> <position x="4.89" y="1.46" z="0.87" /> <rotation x="0" y="253.2478" z="0" /> <scale x="1" y="1" z="1" /> </transform> </gameObject> <gameObject objectName="Sphere" objectAssetURL="Sphere"> <transform> <position x="-2.63" y="3.28" z="-3.95" /> <rotation x="270" y="0" z="0" /> <scale x="1" y="1" z="1" /> </transform> </gameObject> </scene> </root>
3.将预设打包为AssetBundle,并上传到服务器
打包Assetbundle编辑器脚本:
[code]csharpcode:
using UnityEngine; using System.Collections; using UnityEditor; public class Test : Editor { [MenuItem("Custom Editor/Create AssetBunldes Main For Android")] static void CreateAssetBunldesMainForAndroid() { //获取在Project视图中选择的所有游戏对象 Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); //遍历所有的游戏对象 foreach (Object obj in SelectedAsset) { //本地测试:建议最后将Assetbundle放在StreamingAssets文件夹下,如果没有就创建一个,因为移动平台下只能读取这个路径 //StreamingAssets是只读路径,不能写入 //服务器下载:就不需要放在这里,服务器上客户端用www类进行下载。 string targetPath = Application.dataPath + "/StreamingAssets/" + obj.name + "Android" + ".assetbundle"; if (BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies, BuildTarget.Android)) { Debug.Log(obj.name + "资源打包成功"); } else { Debug.Log(obj.name + "资源打包失败"); } } //刷新编辑器 AssetDatabase.Refresh(); } [MenuItem("Custom Editor/Create AssetBunldes Main For iPhone")] static void CreateAssetBunldesMainForiPhone() { //获取在Project视图中选择的所有游戏对象 Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); //遍历所有的游戏对象 foreach (Object obj in SelectedAsset) { string targetPath = Application.dataPath + "/StreamingAssets/" + obj.name + "iPhone" + ".assetbundle"; if (BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies, BuildTarget.iPhone)) { Debug.Log(obj.name + "资源打包成功"); } else { Debug.Log(obj.name + "资源打包失败"); } } //刷新编辑器 AssetDatabase.Refresh(); } [MenuItem("Custom Editor/Create AssetBunldes ALL For Android")] static void CreateAssetBunldesALLForAndroid() { Caching.CleanCache(); string Path = Application.dataPath + "/StreamingAssets/ALLAndroid.assetbundle"; Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); foreach (Object obj in SelectedAsset) { Debug.Log("Create AssetBunldes name :" + obj); } //这里注意第二个参数就行 if (BuildPipeline.BuildAssetBundle(null, SelectedAsset, Path, BuildAssetBundleOptions.CollectDependencies, BuildTarget.Android)) { AssetDatabase.Refresh(); } else { } } [MenuItem("Custom Editor/Create AssetBunldes ALL For iPhone")] static void CreateAssetBunldesALLForiPhone() { Caching.CleanCache(); string Path = Application.dataPath + "/StreamingAssets/ALLiPhone.assetbundle"; Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); foreach (Object obj in SelectedAsset) { Debug.Log("Create AssetBunldes name :" + obj); } //这里注意第二个参数就行 if (BuildPipeline.BuildAssetBundle(null, SelectedAsset, Path, BuildAssetBundleOptions.CollectDependencies, BuildTarget.iPhone)) { AssetDatabase.Refresh(); } else { } } [MenuItem("Custom Editor/Create Scene For Android")] static void CreateSceneALLForAndroid() { Caching.CleanCache(); Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); foreach (Object obj in SelectedAsset) { string Path = Application.dataPath + "/StreamingAssets/" + obj.name + "Android" + ".unity3d"; string[] levels = { @"Assets/Scenes/ExportedScene/" + obj.name + ".unity" }; BuildPipeline.BuildPlayer(levels, Path, BuildTarget.Android, BuildOptions.BuildAdditionalStreamedScenes); Debug.Log("Craete Scene" + Path + "Complete!!"); } AssetDatabase.Refresh(); } [MenuItem("Custom Editor/Create Scene For iPhone")] static void CreateSceneALLForiPhone() { Caching.CleanCache(); Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); foreach (Object obj in SelectedAsset) { string Path = Application.dataPath + "/StreamingAssets/" + obj.name + "iPhone" + ".unity3d"; string[] levels = { @"Assets/Scenes/ExportedScene/" + obj.name + ".unity" }; BuildPipeline.BuildPlayer(levels, Path, BuildTarget.iPhone, BuildOptions.BuildAdditionalStreamedScenes); Debug.Log("Craete Scene" + Path + "Complete!!"); } AssetDatabase.Refresh(); } }
4.将服务器的路径修改到XML配置文件中,并将XML上传到服务器
5.在Unity客户端下载XML并解析,下载并实例化对应的资源
[code]csharpcode:
using UnityEngine; using System.Collections; using System.IO; using System.Collections.Generic; using System.Xml; public class GetXMLDoc : MonoBehaviour { string filePath; public string XMLDocURL = "http://************.xml"; void Awake () { filePath = Application.persistentDataPath + "/XMLDoc1028.xml"; if (File.Exists (filePath)) { File.Delete (filePath); } WWW www = new WWW (XMLDocURL); StartCoroutine (DownloadXMLDoc (www)); } IEnumerator DownloadXMLDoc (WWW www) { yield return www; if (www.isDone) { Debug.Log ("WWW is done"); byte[] bts = www.bytes; int length = bts.Length; CreateXMLDoc (filePath, bts, length); } } void CreateXMLDoc (string path, byte[] info, int lenth) { Debug.Log ("Start to create XML"); Stream sw; FileInfo t = new FileInfo (path); if (!t.Exists) { sw = t.Create (); } else { return; } sw.Write (info, 0, lenth); sw.Close (); sw.Dispose (); Debug.Log ("XML create sucess"); LoadScene (); // 下载完毕之后可以读取并进行实例化了 } void LoadScene () { Debug.Log ("开始加载"); if (File.Exists (filePath)) { XmlDocument xmlDoc = new XmlDocument (); xmlDoc.Load (filePath); XmlNodeList nodeList = xmlDoc.SelectSingleNode ("root").ChildNodes; foreach (XmlElement scene in nodeList) { //因为我的XML是把所有游戏对象全部导出, 所以这里判断一下只解析需要的场景中的游戏对象 //JSON和它的原理类似 // if (!scene.GetAttribute("name").Equals("Assets/StarTrooper.unity")) // { // continue; // } foreach (XmlElement gameObjects in scene.ChildNodes) { // 取得资源地址 string assetRUL = gameObjects.GetAttribute ("objectAssetURL"); string assetName = gameObjects.GetAttribute ("objectName"); Vector3 pos = Vector3.zero; Vector3 rot = Vector3.zero; Vector3 sca = Vector3.zero; foreach (XmlElement transform in gameObjects.ChildNodes) { foreach (XmlElement prs in transform.ChildNodes) { if (prs.Name == "position") { pos.x = float.Parse(prs.GetAttribute("x")); pos.y = float.Parse(prs.GetAttribute("y")); pos.z = float.Parse(prs.GetAttribute("z")); // foreach (XmlElement position in prs.ChildNodes) { // switch (position.Name) { // case "x": // pos.x = float.Parse (position.InnerText); // break; // case "y": // pos.y = float.Parse (position.InnerText); // break; // case "z": // pos.z = float.Parse (position.InnerText); // break; // } // } } else if (prs.Name == "rotation") { rot.x = float.Parse (prs.GetAttribute("x")); rot.y = float.Parse(prs.GetAttribute("y")); rot.z = float.Parse(prs.GetAttribute("z")); // foreach (XmlElement rotation in prs.ChildNodes) { // switch (rotation.Name) { // case "x": // rot.x = float.Parse (rotation.InnerText); // break; // case "y": // rot.y = float.Parse (rotation.InnerText); // break; // case "z": // rot.z = float.Parse (rotation.InnerText); // break; // } // } } else if (prs.Name == "scale") { sca.x = float.Parse (prs.GetAttribute("x")); sca.y = float.Parse(prs.GetAttribute("y")); sca.z = float.Parse(prs.GetAttribute("z")); // foreach (XmlElement scale in prs.ChildNodes) { // switch (scale.Name) { // case "x": // sca.x = float.Parse (scale.InnerText); // break; // case "y": // sca.y = float.Parse (scale.InnerText); // break; // case "z": // sca.z = float.Parse (scale.InnerText); // break; // } // } } } Debug.Log ("准备下载:" + assetRUL); // 开始下载并实例化对象 Debug.Log (assetName + ":pos=" + pos); Debug.Log (assetName + ":rot=" + pos); Debug.Log (assetName + ":sca=" + pos); StartCoroutine (DownloadAsset (new WWW (assetRUL), pos, rot, sca)); //拿到 旋转 缩放 平移 以后克隆新游戏对象 // GameObject ob = (GameObject)Instantiate(Resources.Load(asset), pos, Quaternion.Euler(rot)); // ob.transform.localScale = sca; } } } } } IEnumerator DownloadAsset (WWW www, Vector3 pos, Vector3 rot, Vector3 sca) { yield return www; if (www.isDone) { // yield return Instantiate(www.assetBundle.mainAsset,pos,pos,Quaternion.Euler(rot)); GameObject ob = (GameObject)Instantiate (www.assetBundle.mainAsset, pos, Quaternion.Euler (rot)); ob.transform.localScale = sca; www.assetBundle.Unload (false); } } }