Megabite #24 - Saving and Loading (part 1)

The problem with saving and loading as it pertains to newer programmers is often a lack of understanding.  Like pathfinding or AI, it's one of those things that needs to be customized to each game.

In the older days, games did not take up a great deal of RAM while running.  As such, it wasn't a big deal to save everything the game was doing into a file and just load it up.  This practice is commonly referred to as saving and loading "states".  Games today, however, can fill machines that use anywhere from 2-16+ gigabytes of ram - we wouldn't want our save files that large, so the process of holding onto data had to adapt.

Today I will cover a few things for more beginner-type saving.  While saving to an actual file on the hard drive is quite possible, what we will cover today will be a basic walkthrough of two things - PlayerPrefs and PlayerPrefsX.

PlayerPrefs is an easy feature that comes with Unity3d by default.  With it, we can quickly and easily save integers, strings, and floats.  Just as easily, we can recall that data and use it however we like.  Here is an example with two functions - one will save a basic score, and the other will load that score into a variable:

function Save(theScore : int) {
PlayerPrefs.SetInt("Score", theScore);

var loadedScore : int;
function Load() {
loadedScore = PlayerPrefs.GetInt("Score");

As you can see, its pretty streamlined.  However, the ease of use that PlayerPrefs allows comes with its greatest downfall - saving complex numbers, vectors, arrays, etc aren't easily feasible.  This is where PlayerPrefsX comes it - and it's something you don't automatically get with unity by default.  However, it's free and easy to get set up.

PlayerPrefsX Javascript + Basics

Copy the code from the large code block at the bottom of the page above and save it as PlayerPrefsX.js.  Drop it into your Standard Assets folder and the functions are accessible from any script you choose.

As you can see, this gives you access to the following functions:

  • PlayerPrefsX.SetBool
  • PlayerPrefsX.GetBool
  • PlayerPrefsX.SetVector2
  • PlayerPrefsX.GetVector2
  • PlayerPrefsX.SetVector3
  • PlayerPrefsX.GetVector3
  • PlayerPrefsX.SetQuaternion
  • PlayerPrefsX.GetQuaternion
  • PlayerPrefsX.SetColor
  • PlayerPrefsX.GetColor
  • PlayerPrefsX.SetIntArray
  • PlayerPrefsX.GetIntArray
  • PlayerPrefsX.SetFloatArray
  • PlayerPrefsX.GetFloatArray
  • PlayerPrefsX.SetBoolArray
  • PlayerPrefsX.GetBoolArray
  • PlayerPrefsX.SetStringArray
  • PlayerPrefsX.GetStringArray
  • PlayerPrefsX.SetVector2Array
  • PlayerPrefsX.GetVector2Array
  • PlayerPrefsX.SetVector3Array
  • PlayerPrefsX.GetVector3Array
  • PlayerPrefsX.SetQuaternionArray
  • PlayerPrefsX.GetQuaternionArray
  • PlayerPrefsX.SetColorArray
  • PlayerPrefsX.GetColorArray

All of these will function as you would expect, and in the same way as PlayerPrefs does.  However, you have the versatility of saving Vector3s, Quaternions, String Arrays, and many more.

While PlayerPrefs is a wonderful tool (and actually what PlayerPrefsX uses to function), with this new script in your arsenal you just need to get all of the data you would like to keep into array format.  Saving the positions and rotation of players and game objects becomes this much easier, and saving/loading worlds and levels becomes that much more seamless.

To test this script myself, I created a terrain and used the unity tree generator tool to create a tree gameObject.  When the scene begins, 10,000 trees are created and placed in random positions along the 2000x2000 terrain.  With PlayerPrefsX, I was able to gather the vector3 positions of each tree into a Vector3 array and seamlessly save it into a variable, like so:

 var allTrees : GameObject[] = GameObject.FindGameObjectsWithTag("tree");
 var numTrees : int = allTrees.length;
 print ("There are " + numTrees + " Trees in this world");
 PlayerPrefs.SetInt("NumTrees", numTrees);
 var treeLocs = new Vector3[numTrees];
 for (x =0 ; x < numTrees-1; x++) {
 treeLocs[x] = allTrees[x].transform.position;
 PlayerPrefsX.SetVector3Array ("TreeLocs", treeLocs);

As you can see, I also used the normal PlayerPrefs function to save the raw number of trees that exist at the time.  To load the trees onto a blank slate terrain, I simply use this code in a Load() function:

//Load new trees at saved locations
 var numTrees : int = PlayerPrefs.GetInt("NumTrees");
 var treeLocs = new Vector3[numTrees];
 treeLocs = PlayerPrefsX.GetVector3Array("TreeLocs");
 print("Tree locations recieved for " + numTrees + " trees");

 for (x =0 ; x < numTrees-1; x++) {
 var tree = Instantiate(treePrefab, treeLocs[x], Quaternion.identity);
 print("added trees in previous locations");

I hope this article can be of some help to you.  While it may be difficult to realize each little thing you want to save, if you build your game with saving and loading in mind, the process becomes much easier to manage.

As always, drop questions or comments into the comment fields below :)