How To Do Secure Saving with Binary – Unity C#

Join the Discord. Talk about Game Dev. Talk about Gaming.

This tutorial assumes you have a basic understanding of coding in C#, but don’t know how to do saving and loading.

With most games, you want the player to be able to save their progress, but it’s not obvious how to do that in Unity without a bit of help. I will show you how to easily and securely create a save system in Unity using binary files.

There are multiple ways to do saving and loading in Unity: Player Prefs (which is built in to Unity) and creating JSON or XML files are the most common. They are fine for small projects, but the data is easy for anyone with a text editor to read and modify, so they’re not secure. With binary files, the data is stored as 0’s and 1’s, so it’s much harder for someone to modify the data, but it’s easy for you to create the save system. 

Binary is the saving system I used in my game Puzzledorf, and once implemented, it was quite easy to work with. I store everything from levels unlocked, to highscores and player settings.

There are potentially even more secure ways to save data out there, but for now, saving it as Binary is a step up from Player Prefs and some of the more common text readable methods, and will suit people making single player games. If you are making something that is online or competitive, or that houses very sensitive data,  you might need to look at other options, which I hope to cover in the future.

How it Works

The steps are:

  1. Store the information you want in a classscript
  2. Save all of the data you want as public variables
  3. Mark the class as serializable
  4. Set up another class to handle the actual saving and loading
  5. Use the binary formatter to convert the code into binary to save, and back again when you want to load
  6. The final code is at the end of the document, but I suggest you read the whole tutorial to understand it.

Binary is the computers native language, also known as machine code, a bunch of 1’s and 0’s. Think of like a switch, either on or off. A computer will read a series of 1’s and 0’s and translate that information into something that we would recognize, but it’s very hard for a human to read it.

If you want to save the level the player is up to, level 21, we would store that in the variable int level.  

Then we use something called a binary formatter which would take that information and turn it into 1’s and 0’s and save it as a file. Later, when we want to load that information, we use the binary formatter again and it would turn the 1’s and 0’s back into something we understand, in this case, we would then have access to int level, and it would tell us the player is up to level 21.

What It Can and Can’t Do

Because we’re converting information into the primitive format of binary, we are limited in the type of information we can save. We can’t save any Unity specific types of information and variables. For example, we can’t save Vector3. But we could, however, store an array of 3 floats that we can convert into a Vector3. Mainly we will be able to save primitive data types that are native to C#, stuff like:

  • ints and floats
  • bools
  • arrays[]
  • strings

Some of it you will find out with trial and error, but if something isn’t saving, it might not be able to be converted into binary.

Begin – Game Data Class

We shall create a small test project that will save and load a score and print it out as a string. We will use key presses to change the score, save the game, wipe the score, and to load the previous score.

Let’s get started. Create a new project in Unity. Make a new class, and call it Game Data. All of the data in this class will become the save file. You could have multiple files, but for the sake of this exercise, we’ll use one file.

Get rid of the Start() and Update() functions, you don’t need them, and it saves the computer checking them. Remove mono behaviour from the class declaration. You should be left with something like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameData : MonoBehaviour
{
}

If we don’t remove MonoBehaviour, we can’t serialize the class.

Now you need to make it serializable – add the code in blue:

...

[System.Serializable]
public class GameData 
{
}

From now on, three dots means there is code I haven’t bothered repeating. If I’ve got black and blue code, blue means new code

So what does serializable mean? To serialize something means to convert it to binary. This makes it easy to do different things with that information – there are various uses. In our case, it makes it easy for us to save the data we want and make it non-human readable.

When we mark a class as serializable, we are saying we want to convert that class into binary at some point.

Now let’s add our int variable for the score and start it at 0.

public class GameData
{
   public int score = 0;
}

Later we will want to add to or subtract from the score, and add an option to reset the score to 0. Let’s add two simple functions to do that:

...

public void AddScore(int points)
{
   score += points;
}

public void ResetData()
{
   score = 0;
}

score += points; means we will add the variable points to the score. We don’t need to create a subtract function, because if points is negative, it will automatically subtract from the score. When you add a negative number, you subtract. So 1 + -1 = 0.

Here’s the full code for the class (repeated at the end of the tutorial):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class GameData
{
  public int score = 0;

  public void AddScore(int points)
  {
    score += points;
  }

  public void ResetData()
  {
    score = 0;
  }
}

Save System Class – Saving

Now make a new class and call it SaveSystem. This will handle the actual saving and loading, whereas our previous class just holds data.

Remove the Start() and Update() functions.
Remove using Sytem.Collections; 
Remove using System.Collection.Generic;
Add the library extension using System.IO;
Add the library extension using System.Runtime.Serialization.Formatters.Binary;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class SaveSystem : Monobehaviour 
{ 
}

System.IO is a code library to handle files, ie, creating and opening files.

System.Runtime.Serialization.Formatters.Binary is a code library to handle binary conversions.

Add the following code to your class:

public class SaveSystem : MonoBehaviour
{
  string filePath;

  private void Awake()
  {
    filePath = Application.persistentDataPath + "/save.data";
  }
}

The new string variable, filePath, is where our file will be saved.

We then create a new Awake function, which is similar to Start. The difference is when Unity calls the functions. Awake will always be called before Start, and is only ever called once in the lifetime of an object. Awake is only called on active game objects, however it will still be called if the script attached to the game object is not active. Read more about Awake here.

In Awake we set where the file will be saved. We have to do it in Awake, because Unity won’t let us do it when we declare the variable in our class. We could have written:

 filePath = "C:/System/save.data";

But that won’t work on Mac or mobile.

Application.persistentDataPath lets Unity choose a place in the system directory for you that won’t suddenly change, regardless of what device the game is being played on. It also makes it harder for someone to track down where your save data is to modify it.

“/save.data” is the name of the file you are creating and your own custom file type. /save is the name of the file, data is the file type we created. Because it is a binary file, you can give it any file type you want and it won’t matter. We could have called it “/save.smile”.

The next thing we want to do is create a function for saving. Create a new function called SaveGame():

public void SaveGame(GameData saveData)
{
}

We need it to be public so we can call it from other scripts, and we’re going to pass in a GameData variable. This way, we’ll let other scripts populate our GameData file, and then when it’s time to save, we’ll just pass the information in through the function paramater.

Now add the following lines of code to the function:

public void SaveGame(GameData saveData)
{
   FileStream dataStream = new FileStream(filePath, FileMode.Create);

   BinaryFormatter converter = new BinaryFormatter();
   converter.Serialize(dataStream, saveData);

   dataStream.Close();
}

FileStream is a stream or a bunch of data that contains all of the data in a file. We can use a particular file stream to read and write from a file, and that data will be stored within the variable.

We create a new FileStream called dataStream. We pass in the filePath, which is where the file will be created. We then choose a FileMode, which is what we want to do with the file, in this instance, we use FileMode.Create to create a new file.

The BinaryFormatter, quite simply, is what converts our human readable code into binary. First we make a new one, called converter, and then we use it.

converter.Serialize(dataStream, saveData);

The above line of code is where the actual conversion happens. To serialize means to convert to binary. The Serialize() function writes data to the file through the binary conversion process. Then we pass in our empty file which is stored in dataStream, and then we pass in saveData, the data we stored in our class, to be saved to that file. Now the file stored in dataStream is no longer empty, and it is stored as an external file on our device.

dataStream.Close();

We need to close off the file to say we’ve stopped using it, or we will get strange errors. The above line of code says that we’re done writing data to dataStream.

Next we need to be able to load saved data.

Save System Class – Loading

Create a new function called LoadGame() and add the following code:

public GameData LoadGame()
{
  if(File.Exists(filePath))
  {
    // File exists  
  }
  else
  {
    // File does not exist
  }
}

We add an if / else statement to check if there is a save file when we want to load the game. Otherwise, if we try loading the game and there is no save file, we will get strange errors.

File.Exists(filePath) will check to see if a file exists at the location given to it, in this case, filePath, and then it will return either true or false.

First, lets add the code for when the save file is found. Add the following code to your if statement:

if(File.Exists(filePath))
{
  // File exists 
  FileStream dataStream = new FileStream(filePath, FileMode.Open);

  BinaryFormatter converter = new BinaryFormatter();
  GameData saveData = converter.Deserialize(dataStream) as GameData;

  dataStream.Close();
  return saveData;
}

So first we make a new FileStream to contain the data for the file. This time, however, instead of FileMode.Create, we use FileMode.Open because we are opening an existing file. dataStream will now contain all of that data.

We then make a new BinaryFormatter so that we can convert the binary save file back into human readable code.

Then we create a variable, saveData, which is a copy of our GameData class, to store all of the information.

GameData saveData = converter.Deserialize(dataStream) as GameData;

We use the converter to deserialize the saved data in dataStream. To deserialize means to convert back from binary into human readable code. We have to say at the end:

as GameData;

This is to convert the binary file into a GameData class – otherwise Unity wouldn’t know what to do with the data, what kind of information it was, and you would get errors.

Finally we close off the data stream, because we are finished writing to the file, and then we return the saved data file we found. Return means that, whenever we use this function and find a save file, it will give us that save file so that we can use it. You can read more about returning from functions here.

Next , if there is no save file, we need to know that, so we will create a custom error message. Add the code in blue to your else statement:

if(File.Exists(filePath))
{
  // File exists 
  ...
}
else
{
  // File does not exist
  Debug.LogError("Save file not found in " + filePath);
  return null;
}

You might have used Debug.Log() before to write custom messages to the console. Debug.LogError() will show a custom error written in Red. In this case, we announce the save file wasn’t found, and then add the filePath to the error message, telling us where Unity is looking for the file. Creating your own error messages in key places, if there is something specific you need to check for, is more helpful than Unity’s error messages, which can be vague at times.

We then return null because no save file was found – when a function has a return type that isn’t void, you have to always return something, in this case, null will do, because we found nothing.

We now need to make this class into a singleton. A singleton simply means that you can only have one copy of that class in your scene at any time. Your code will check to make sure there are no other copies of the class in the scene, and if there are, they will destroy themselves. Having multiple save systems in your scene would cause weird errors with saving and loading. 

To create a singleton, we need to add a new variable to the start of our class called instance, and add some code to the Awake function:

public class SaveSystem : MonoBehaviour
{
  // Make this class a singleton / single instance
  static public SaveSystem instance;
  string filePath;

  private void Awake()
  {
    filePath = Application.persistentDataPath + "/save.data";

    // Check there are no other copies of this class in the scene
    if (instance == null)
    {
      instance = this;
    }
    else
    {
      Destroy(gameObject);
    }
  }
}

The terms instance and copy are interchangeable in this case. A single instance of an object in a scene is the same as saying there is a single copy of it in the scene. We create a new variable called instance to store a self reference, to say, “I exist.”

I will explain the term static in a minute.

Next we have an if / else statement to check if an instance or copy of this class does exist in the scene.

if (instance == null)

If instance is null, there is no copy of this class in the scene, so we will set instance to be a self reference. Now if a second copy of this class was made, it would go to the else statement.

If it goes to the else statement, it destroys itself. That is what a singleton is, leaving us with just one copy in the scene.

Now back to static. A static variable means there can only be one copy of this variable, a little bit like a singleton, and it will always have the same value, no matter how many copies of the class there are. If it was an int set to 5, then every copy of the class would have that int set to 5. Changing one would change them all. There could still, however, be multiple instances of the class, so we use the if / else check to make sure that doesn’t happen in this case.

A static variable also means that we can access it if we call the class through code without instantiating it, ie:

SaveSystem.instance.Save();

We can reference the instance variable directly because it is static, without creating a copy of the class in our code. You could not, however, do:

SaveSystem.filePath = "C:\hello.doc";

Becase filePath is not static, it can’t be accessed in that way. It must be accessed through the instance variable. This will make more sense when we use the static variable later. There’s further reading on static here.

The simplest way to implement a singelton once created is to attach it to a game object in the scene, ideally something that is created at the start of the scene, or at the start of the game, and is never destroyed unless you no longer need it. I recommend you now create a game object called Game Master and attach the SaveSystem script to it. 

Here is the finished SaveSystem class (repeated again at the tutorials end):

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class SaveSystem : MonoBehaviour
{
  // Makes it a singleton / single instance
  static public SaveSystem instance;
  string filePath;

  private void Awake()
  {
    // Check there are no other instances of this class in the scene
    if (!instance)
    {
      instance = this;
    }
    else
    {
      Destroy(gameObject);
    }

    filePath = Application.persistentDataPath + "/save.data";
  }

  public void SaveGame(GameData saveData)
  {
    FileStream dataStream = new FileStream(filePath, FileMode.Create);

    BinaryFormatter converter = new BinaryFormatter();
    converter.Serialize(dataStream, saveData);

    dataStream.Close();
  }

  public GameData LoadGame()
  {
    if(File.Exists(filePath))
    {
      // File exists 
      FileStream dataStream = new FileStream(filePath, FileMode.Open);

      BinaryFormatter converter = new BinaryFormatter();
      GameData saveData = converter.Deserialize(dataStream) as GameData;

      dataStream.Close();
      return saveData;
    }
    else
    {
    // File does not exist
    Debug.LogError("Save file not found in " + filePath);
    return null;
    }
  }
}

Putting It Together – Game Master Class

Let’s make a new class called GameMaster, the Master class that will control our game.

Remove the Start function but leave Update there. Now, we’ll make a function called PrintScore and a variable called saveData.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameMaster : MonoBehaviour
{
  GameData saveData = new GameData();

  Update()
  {
  }

  void PrintScore()
  {
    Debug.Log("The current score is " + saveData.score);
  }
}

saveData will be where we store all of our games data. We are creating it within the class, rather than attaching it separately to a game object, because it keeps everything in one place. You could have multiple files if you wanted, but we’ll just have one. In this case it contains our score.

We will use the Update function to add or subtract from our score through keyboard input, and it will put the score changes directly into saveData.

When we want to save, we’ll save whatever score is in our copy of saveData to a binary file on our computer. Later when we want to load it, we will  update our copy of saveData from the file.

The PrintScore function will simply print a string to the console telling us the score stored in saveData.

Now let’s add some keyboard input to Update. First, let’s make it so that we can add to and subtract from the score.

void Update()
{
  if(Input.GetKeyDown(KeyCode.UpArrow))
  {
    saveData.AddScore(1);
    PrintScore();
  }
  if (Input.GetKeyDown(KeyCode.DownArrow))
  {
    saveData.AddScore(-1);
    PrintScore();
  }
}

Our if statement checks if the up or down arrow is being pressed. GetKeyDown checks if a key has been pressed. GetKey (which we’re not using) checks if a key is being held down. KeyCode is how we work out which key is being pressed.

Then, we simply add 1 or -1 to the score, and then print the score to the console. Remember when I said that if you add a negative, you do a subtraction?
So 3 + -1 = 2. You could rewrite it as 3 – 1 = 2.

Now lets add the saving and loading.

void Update()
{
  ...

  if(Input.GetKeyDown(KeyCode.S))
  {
    SaveSystem.instance.SaveGame(saveData);
    Debug.Log("Saved data.");
  }
  if(Input.GetKeyDown(KeyCode.L))
  { 
    saveData = SaveSystem.instance.LoadGame();
    Debug.Log("Loaded data.");
    PrintScore();
  }
}

If you press the S key, we will call our Save function from the SaveSystem. We don’t have to create an instance of SaveSystem in our code to access the variable instance because we made instance static. That should make sense to you now why we made it static. Otherwise we would have had to do something like:

SaveSystem system = new SaveSystem();
system.SaveGame(saveData);

This avoids that, making our code tidier and neater, not just because there are less lines of code, but because instance is static, and because SaveSystem is a singleton, we will never accidentally have multiple copies of our save system running around causing weird errors.

We have just one last thing to add to Update – not essential, but it’s nice. The ability to reset the score to 0.

void Update()
{
  ...

  if(Input.GetKeyDown(KeyCode.X))
  {
    saveData.ResetData();
    PrintScore();
  }
}

So if we press the X key, it will reset our score back to 0, and print that new score. That will give us a quick way to test if the code works.

Before you do anything else, attach your new GameMaster script to the Game Master object in your scene (remember the SaveSystem script should also be attached to the Game Master object). 

Here is the final code for the class:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameMaster : MonoBehaviour
{
  GameData saveData = new GameData();

  // Update is called once per frame
  void Update()
  {
    if(Input.GetKeyDown(KeyCode.UpArrow))
    {
      saveData.AddScore(1);
      PrintScore();
    }
    if (Input.GetKeyDown(KeyCode.DownArrow))
    {
      saveData.AddScore(-1);
      PrintScore();
    }
    if(Input.GetKeyDown(KeyCode.S))
    {
      SaveSystem.instance.SaveGame(saveData);
      Debug.Log("Saved data.");
    }
    if(Input.GetKeyDown(KeyCode.L))
    {
      saveData = SaveSystem.instance.LoadGame();
      Debug.Log("Loaded data.");
     PrintScore();
    }
    if(Input.GetKeyDown(KeyCode.X))
    {
      saveData.ResetData();
      PrintScore();
    }
  }

  void PrintScore()
  {
    Debug.Log("The current score is " + saveData.score);
  }
}

Test It

Now it’s time to test it. Two quick things to do before we test. First, make sure you can see the console. Down where the Project tab is at the bottom of the screen, next to that should be Console.

Console

If it’s there, good. Make sure Collapse is not selected. If you can’t see the console, go up the top of the screen to Window > General > Console and turn it on.

Now try the game out. Press the up and down arrows to change the score, press S to save, then change the score again or wipe it pressing X, then press to load. Did it work? If not, double check your code – and I’ve got all of the finished code below.

If it didn’t work, first of all make sure that you have the GameMaster and SaveSystem scripts attached to your Game Master object in your scene.

If it worked, close the game, then play it again. Before you do anything, try loading the score. It should be the same one you saved. Saving should be 100% working. This is because all of the save data is stored in an external file, so it doesn’t disappear when the game is closed.

Congratulations, you can now save and load! Take this knowledge and make some great games. The final code is below.

Join the Discord. Talk about Game Dev. Talk about Gaming.

All of the Code

Game Data Class

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class GameData
{
  public int score = 0;

  public void AddScore(int points)
  {
    score += points;
  }

  public void ResetData()
  {
    score = 0;
  }
}

Save System Class

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class SaveSystem : MonoBehaviour
{
  // Makes it a singleton / single instance
  static public SaveSystem instance;
  string filePath;

  private void Awake()
  {
    // Check there are no other instances of this class in the scene
    if (!instance)
    {
      instance = this;
    }
    else
    {
      Destroy(gameObject);
    }

    filePath = Application.persistentDataPath + "/save.data";
  }

  public void SaveGame(GameData saveData)
  {
    FileStream dataStream = new FileStream(filePath, FileMode.Create);

    BinaryFormatter converter = new BinaryFormatter();
    converter.Serialize(dataStream, saveData);

    dataStream.Close();
  }

  public GameData LoadGame()
  {
    if(File.Exists(filePath))
    {
      // File exists 
      FileStream dataStream = new FileStream(filePath, FileMode.Open);

      BinaryFormatter converter = new BinaryFormatter();
      GameData saveData = converter.Deserialize(dataStream) as GameData;

      dataStream.Close();
      return saveData;
    }
    else
    {
      // File does not exist
      Debug.LogError("Save file not found in " + filePath);
      return null;
    }
  }
}

Game Master Class

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameMaster : MonoBehaviour
{
  GameData saveData = new GameData();

  // Update is called once per frame
  void Update()
  {
    if(Input.GetKeyDown(KeyCode.UpArrow))
    {
      saveData.AddScore(1);
      PrintScore();
    }
    if (Input.GetKeyDown(KeyCode.DownArrow))
    {
      saveData.AddScore(-1);
      PrintScore();
    }
    if(Input.GetKeyDown(KeyCode.S))
    {
      SaveSystem.instance.SaveGame(saveData);
      Debug.Log("Saved data.");
    }
    if(Input.GetKeyDown(KeyCode.L))
    {
      saveData = SaveSystem.instance.LoadGame();
      Debug.Log("Loaded data.");
      PrintScore();
    }
    if(Input.GetKeyDown(KeyCode.X))
    {
      saveData.ResetData();
      PrintScore();
    }
  }

  void PrintScore()
  {
    Debug.Log("The current score is " + saveData.score);
  }
}

21 thoughts on “How To Do Secure Saving with Binary – Unity C#

Add yours

  1. Aha!
    That’s a good idea actually!

    Yesterday I tried another method :
    Which was to delete the saved data file, and create a new one each time I had to save, and erase the original one.
    And I could retrieve all the indexes of the List I saved in a newer state.
    It means that opening the stream, and adding the new line or replacing the content in the index is not working.
    I think I saw somewhere that opening the file and add a content is not enough, there is a one more step to be done, but I can’t recall which one
    I am going to investigate!

    So List works!
    That’s a new development for me!
    I will keep you posted!

    Like

  2. hello!
    thank you for your Tutorial!

    I have a question though :
    Is it possible to save a List with BinaryFormatter in your SaveSystem?

    – Let’s say I have for each level a data set defined by GameData : int score, int item, bool levelCleared.
    – I want to save the GameData for each level. I put them within GameMaster in a List. The List index corresponds to each Level for example
    – I send the List to SaveSystem.SaveGame(exampleList)

    For now, SaveGame receives the List, but I don’t know if it actually saved. When I load the data set, I only get 1 GameData and the rest of the List is gone…
    I don’t know if it does not save at the first place, or it does not load…

    If you had an idea, it would be lovely…… I am on this situation for so long, that the dev is stuck…

    Thank you!

    Like

    1. I can’t remember all data types that it can save but pretty sure Lists won’t work, it seems to be only real primitive data types. Arrays will save, however, which are basically the same.

      If you have a list, I would copy all of the data into an array and save that, then when you load, copy all of the data in the array back into a list. It involves a bit of fiddling but completely doable.

      Like

    2. And going by what you said, it does seem to confirm that Lists won’t work, but as my other message says, you could work around that with arrays.

      Like

      1. Yes, it could work with arrays.
        It’s pity tho. I find data handling easier with List..
        Gonna try that then!
        I’ll come back to you if it works or not!

        thank you so much! You rock!

        Liked by 1 person

        1. Lists are way easier. You can keep using Lists in your main code, but save it into an array.

          So, when you want to save, do a loop. Loop over everything in you List, and for each item in the List, copy it into the same slot in the array.

          When you’re loading, do the reverse. Loop over the array and “Add” each item back into the list. Done. Shouldn’t be too bad

          Like

  3. So I got this working perfectly in the editor but it refuses to save on Android. I’ve made sure the external SD permissions is selected in player settings but no dice…any ideas? Hours of Googling and trying all the fixes I’ve found have not worked for me. This is Android 12.

    Like

    1. I’m afraid I haven’t tried this with Android yet. Have you tried posting a question in the Unity Forums? Maybe under Platforms: https://forum.unity.com/categories/platforms.77/

      Or general: https://forum.unity.com/forums/editor-general-support.10/

      Hopefully someone there has some experience with it. However, to the best of my knowledge, saving with Binary in this style, and the file path method provided, should be universal across a lot of different platforms.

      Like

  4. What is the best way to saving our data in local and with full security? With this i get it most people doesnt understand the binary line but someone can easy use binary decoder. Even using this binary decoder in browser not a uncommon knowlegde. Do you have any tactics for more secure data?

    Like

    1. I have not tried to save a class as data in and of itself, I do not think it would work. I believe you would need to save all of the variables within that class separately, but maybe you will have to experiment with it. You will have to experiment with Lists as well. What you can save is:

      ints and floats
      bools
      arrays[]
      strings

      I suspect you cannot save lists but you could try. But, you could save the List as an Array[], by looping over each element and adding it to the array, then when you load, loop over the array and push it back into a List.

      Like

    2. To follow on my comment about saving classes, again I suspect you can’t. However, if it were possible, you would still need to be careful what types of variables are in the class. But I don’t think you will be able to save a class in this way – I haven’t tried, though.

      Like

    1. Hm you may be right, I may have to research this further. I was under the impression that some of the other save methods are less secure because they are a lot more human readable and easier to backwards compile, but I’ll look into it. Cheers.

      Like

  5. Fantastic tutorial! My only suggestion would be to ensure that people create a game object and apply the Game Master and SaveSystem scripts to it, because not everyone, familiar or otherwise with unity, will necessarily know to do that.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: