Now that we have the groundwork all placed and ready, let's get to the real meat of the level editor: saving and loading! Perform the following steps:
LevelEditor
class in your IDE. The first step will be to include some additional functionality at the beginning of our file://You must include these namespaces //to use BinaryFormatter using System; using System.Runtime.Serialization.Formatters.Binary; using System.IO;
private string levelName = "Level1";
OnGUI
function:GUILayout.BeginArea(new Rect(10, 20, 100, 100)); levelName = GUILayout.TextField(levelName); if (GUILayout.Button ("Save")) { SaveLevel(); } if (GUILayout.Button ("Load")) { //If we have a file with the name typed in, load it! if(File.Exists(Application.persistentDataPath + "/" + levelName + ".lvl")) { LoadLevelFile(levelName); PlayerStart.spawned = false; // We need to wait one frame before UpdateOrbTotals // will work (Orbs need to have Tag assigned) StartCoroutine(LoadedUpdate()); } else { levelName = "Error"; } } if (GUILayout.Button ("Quit")) { enabled = false; } GUILayout.EndArea();
SaveLevel
, as follows:void SaveLevel() { List<string> newLevel = new List<string>(); for(int i = yMin; i <= yMax; i++) { string newRow = ""; for(int j = xMin; j <= xMax; j++) { Vector3 pos = new Vector3(j, i, 0); Ray ray = Camera.main.ScreenPointToRay(pos); RaycastHit hit = new RaycastHit(); Physics.Raycast(ray, out hit, 100); // Will check if there is something hitting us within // a distance of .1 Collider[] hitColliders = Physics.OverlapSphere(pos, 0.1f); if(hitColliders.Length > 0) { // Do we have a tile with the same name as this // object? for(int k = 0; k < tiles.Count; k++) { // If so, let's save that to the string if(tiles[k].name == hitColliders[0].gameObject.name) { newRow += (k+1).ToString() + ","; } } } else { newRow += "0,"; } } newRow += " "; newLevel.Add(newRow); } // Reverse the rows to make the final version rightside // up newLevel.Reverse(); string levelComplete = ""; foreach(string level in newLevel) { levelComplete += level; } // This is the data we're going to be saving print(levelComplete); //Save to a file BinaryFormatter bFormatter = new BinaryFormatter(); FileStream file = File.Create(Application.persistentDataPath + "/"+ levelName + ".lvl"); bFormatter.Serialize (file, levelComplete); file.Close (); }
To do this, we will go through the map, see what tiles are at a certain place, and add them to a string for each column using a list to store each of the rows. Then, we put them all together into a single string, which we could just store in PlayerPrefs
.
However, instead of using the PlayerPrefs
class as we did before, we will store our data in an actual file using the FileStream
class.
To determine where to save our file, we will use the Application.persistentDataPath
variable. This value will point to something differently, depending on what platform you're working with. For instance, on a Windows 8 computer, it will save to C:UsersYOUR_USER_NAMEAppDataLocalLowCOMPANY_NAMEPROJECT_NAME
. For more information, check out http://docs.unity3d.com/ScriptReference/Application-persistentDataPath.html.
For more information on FileStreams
, check out the Microsoft Developers Network's page on it at http://msdn.microsoft.com/en-us/library/system.io.filestream(v=vs.110).aspx.
We don't want the file to be easy to read, so we'll use the BinaryFormatter
class, which will convert our object into a byte array and be a stream of bytes, which will be much harder for potential hackers to read.
For more information on the BinaryFormatter
class, check out the Microsoft Developers Network's page on it at http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter(v=vs.110).aspx.
void LoadLevelFile(string level) { // Destroy everything inside our currently level that's // created dynamically foreach(Transform child in dynamicParent.transform) { Destroy(child.gameObject); } BinaryFormatter bFormatter = new BinaryFormatter(); FileStream file = File.OpenRead(Application.persistentDataPath + "/"+ level + ".lvl"); // Convert the file from a byte array into a string string levelData = bFormatter.Deserialize(file) as string; // We're done working with the file so we can close it file.Close (); LoadLevelFromString(levelData); // Set our text object to the current level. levelName = level; } public void LoadLevelFromString(string content) { // Split our string by the new lines (enter) List <string> lines = new List <string> (content.Split (' ')); // Place each block in order in the correct x and y // position for(int i = 0; i < lines.Count; i++) { string[] blockIDs = lines[i].Split (','), for(int j = 0; j < blockIDs.Length - 1; j++) { CreateBlock(int.Parse(blockIDs[j]), j, lines.Count - i); } } }
LoadedUpdate
so that Orbs
will be updated after they've been created, as follows:IEnumerator LoadedUpdate() { //returning 0 will make it wait 1 frame yield return 0; GameController._instance.UpdateOrbTotals(true); }
As you can see, when we play the game you'll see a new menu appear on the left-hand side? We can now give a name to all of our files that we want, type in their name, hit Save to save it to a file, and Load to load the data for the level if it exists! Finally, we can click on Quit to exit out of the editor whenever we want.
Take a look at the following screenshot:
As you can see here, the files are saved in our Application.persistentDataPath
location (print it out to know exactly where it is)!