Level editor – saving/loading levels to file

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:

  1. Open our 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;
  2. The first thing we'll want to add is a variable, as follows:
    private string levelName = "Level1";
  3. Now, we'll need to add the following code to the 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();
  4. We are missing some of these functions, so let's start with 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.

FileStreams

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.

BinaryFormatter

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.

  1. Now we need to add in the following functions to load the file that we'll be creating from the save functionality:
    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);
          }
        }
      }
  2. Finally, we need to add in 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);
    }
  3. Save the file and exit the editor. Save the project and play the game! Take a look at the following screenshot:
    BinaryFormatter

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:

BinaryFormatter

As you can see here, the files are saved in our Application.persistentDataPath location (print it out to know exactly where it is)!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset