A friend of mine was asking about how you can save the scene hierarchy and object/script properties in Unity, he had written most of the code instantiating prefabs and was already busy setting variables via interactions in his scripts. I explained that although I do have a generic reflection based method, that can save/restore the hierarchy, gameobjects, transforms, materials & script variables ( used on the space application ), it would be too verbose and unfitting for this task, plus if his script and prefab scripts were not written the right way this approach may not work.
With simpler configuration apps a much simpler class based object oriented approach is all that is required and most cases it is better to start with that system in mind than to retro fit. It is also much better if bringing in xml /json from other sources to reflect the xml/json structure with a c# class which you can then attach methods to manipulate the data, rather than hand parse each node (after learning the hard way), something that might be appropriate with collada objects perhaps.
Update: The methods below how to correctly set xml attributes in c# to coerce your values from the xml to your class see the latest post:-
Unity3d XML Object/Class/Type Serialisation Attributes
Below is an older very simple one script demonstration of instantiation and recording values to and from XML in Unity, apologies for any errors or messy logic I think I only had an hour to write it one afternoon, but hopefully it will convey the basics, but I would use the above attributes in c# see the link above over the method below for helping the xml parser to know what values go where and what to ignore etc:-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Xml; using System.Xml.Serialization; using System.IO; public class HeirarchyIO : MonoBehaviour { //MY MOCK UP DATA STRUCTURE private List<myobject> allobjects; //array of myobjects were going to fill and save, then load from public class myobject { //my object class, note doesn't include public vars //like gameobject or transform these do not serialize //so you keep them private and use methods to get or put them public int myint=1; public float myfloat; //you can assign default values here if you like also public float mysize; public string mytext="objectname"; public myobject[] mychildren; public Vector3 mypos; public Quaternion myrot; private GameObject mygameobject; //not serialisable public myobject() { //called when new myobject is created, notice no return type is given and function name is //the same as the class name, to make this fire on new!! also called automatically //when class is remade from serialised xml so will spawn objects, from recorded values createmygameobject(); } public void dosomethingtomyobject() { //just to show you that classes can also have methods/functions //we set the data of the objects in these functions never in creation function //unless its always the same! myfloat=Random.Range(-5.0f, 5.0f); //give the float var a random number on creation mysize=Random.Range(0.5f,1.5f); mypos.y=Random.Range(-3.0f,3.0f); setvalues(); } public void setvalues() { //we don't mix the setting of values function with instantion of gameobjects function mypos.x=myfloat; mygameobject.transform.position=mypos; mygameobject.transform.rotation=myrot; mygameobject.transform.localScale=new Vector3(mysize,mysize,mysize); } public void createmygameobject() { if (!mygameobject) { //important we only want to make a new one if it doesn't already exist mygameobject=new GameObject(mytext); //give it a child primitive object so we can see it reflect our data values GameObject tempnew=GameObject.CreatePrimitive(PrimitiveType.Cube); tempnew.transform.parent=mygameobject.transform; } //setvalues(); } public GameObject GetMyGameobject() { //this allow us to access mygameobject from outside //even though its a private var return mygameobject; //you could also use get and set statements; } } // Use this for initialization void Start () { allobjects=new List<myobject>(); } // Update is called once per frame void OnGUI() { GUILayout.BeginVertical(); if (GUILayout.Button("Create 5 new myobjects & add to array")) { for (int i=0;i<5;++i) { allobjects.Add(new myobject()); //set up new instance of class which in turn generates gameobject etc allobjects[i].dosomethingtomyobject(); //give em some random settings } } if (allobjects.Count>0) { if (GUILayout.Button("Assign Random Values To All")) { for (int i=0;i<allobjects.Count;++i) { allobjects[i].dosomethingtomyobject(); //gives all objects new values } } if (GUILayout.Button("Clear Array Data")) { for (int i=0;i<allobjects.Count;++i) { Destroy(allobjects[i].GetMyGameobject()); //get rid of any gameobject we created } allobjects=new List<myobject>(); } } if (GUILayout.Button("Save Array Data")) { SerializeObject<System.Collections.Generic.List<myobject>>(Application.dataPath+"/mysavedxml.xml",allobjects); } if (GUILayout.Button("Load Array Data")) { allobjects=DeserializeFile<System.Collections.Generic.List<myobject>>(Application.dataPath+"/mysavedxml.xml"); //after we create the object we need to restore to actual settings, this is because it doesnt have the values assigned //till after ther object is created during the deserialise process for (int i=0;i<allobjects.Count;++i) { allobjects[i].setvalues(); //set to last known values } } GUILayout.EndVertical(); } ////////GENERAL RE_USABLE SERIALISE AND FILE METHODS //XML(Class) file to Object(Class) public static T DeserializeFile<T>(string filename) { T obj=default (T); string text=""; if (File.Exists(filename)) { try { XmlSerializer serializer = new XmlSerializer(typeof(T)); FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read); byte[] bytes = new byte[file.Length]; file.Read(bytes, 0, (int)file.Length); MemoryStream assetStream = new MemoryStream(bytes); //ms.Write(bytes, 0, (int)file.Length); //if line above does not work XmlTextReader xmlReader = new XmlTextReader(assetStream); obj = (T)serializer.Deserialize(xmlReader); xmlReader.Close(); assetStream.Close(); file.Close(); } catch (System.Exception err) { text="error:"+err.ToString(); } } else { text="error:File Does Not Exist"; } Debug.Log ("DeserializeFile: "+text); return obj; } //XML(Class) String file to Object(Class) public static T DeserializeText<T>(string xml) { T obj=default (T); string text=""; try { XmlSerializer serializer = new XmlSerializer(typeof(T)); XmlTextReader xmlReader = new XmlTextReader(new StringReader(xml)); obj = (T)serializer.Deserialize(xmlReader); xmlReader.Close(); } catch (System.Exception err) { text="error:"+err.ToString(); } Debug.Log ("DeserializeFile: "+text); return obj; } //Object(Class) to XML(Class) file public static void SerializeObject<T>(string filename, T data) { XmlSerializer serializer = new XmlSerializer(typeof(T)); TextWriter textWriter = new StreamWriter(filename); serializer.Serialize(textWriter, data); textWriter.Close(); } } |