NocabSoftware.com


For those who've been here before:

using LightJson;

public ExampleNocabChild(JsonObject jo) {
    // TODO Validate the JO before loading

    // Load the data from the JO into this class
    this.data = jo["data"];
}

public JsonObject toJson() {
    JsonObject result = new JsonObject();

    // Convert/ add this class’s data into a JO
    result["data"] = data;

    return result;
}
  

For the rest of us:

There are several good ways to save game data in Unity, an overview of options can be found here. However in my opinion, one of the most flexible yet resilient ways to persist data is by writing it to a file. Design choices begt more design choices, and a developer must decide exactly what to write to a file. A popular option is serialization, but I’m going to use JSON as the format to write data to a file. JSON is lightweight, easy to read, and well known across the programming ecosystem. I will note, that because JSON is easy to read, it's also easy to edit which makes it a poor option if you want to discourage players from tinkering with the game files. I’m not worried about this for my games, in fact I would encourage my players to tinker with the game code as an introduction to programming or game design. But the decision is up to you.


The first step to writing JSON to a file requires us to actually convert game objects into JSON. To assist me, I’m using a library called LightJson created by MarconLopezC found here. To add this to your unity project, first check the license, then click the "Clone or download" button and "Download ZIP". Unzip the package and copy the license, Readme, and cs files into your Unity project assets folder.

Now, let's get to coding.

I’ll create a new file called JsonConverterExample, with a new class called ExampleNocabChild that I will be attempting to convert into JSON (more experienced programmers may have a hint of where this tutorial is going). For now the class is empty.

using LightJson;
using UnityEngine;

public class ExampleNocabChild {

}

I want to convert the object into JSON, so I’ll create a function called toJson() which will eventually convert a ExampleNocabChild into a JSON form. The LightJson library provides a suite of useful tools, functions and objects that I won’t go into detail over. All you need to know for now is the JsonObect type which can be converted into a string via the `JsonObject.ToString()` function.

public class ExampleNocabChild {

    public JsonObject toJson() {
        JsonObject result = new JsonObject();
        // Convert/ add this class’s data into a JO
        return result;
    }

}

Let’s create a tester MonoBehavior, attach it to an empty game object and see what happens! (You may need to put this MonoBehavior in a new appropriatly named file)

public class NocabTester : MonoBehaviour {

    private void Start() {
        ExampleNocabChild testChild = new ExampleNocabChild();
        Debug.Log(testChild.toJson().ToString());
    }

}



Not very exciting, an empty JSON object is simply a pair of curly braces. Let’s add some data to the ExampleNocabChild class and update the the toJson() function.

public class ExampleNocabChild {

    public int data;

    public ExampleNocabChild(int data) { this.data = data; }

    
    public JsonObject toJson() {
        JsonObject result = new JsonObject();

        // Convert/ add this class’s data into a JO
        result["data"] = data;

        return result;
    }
}
public class NocabTester : MonoBehaviour {
    private void Start() {
        ExampleNocabChild testChild = new ExampleNocabChild(10);
        Debug.Log(testChild.toJson().ToString());
    }
}

If we run our test script again we can see a slightly more interesting json object.






So we have a way to convert an object into JSON, before we get too invested we need to make sure we can convert from JSON back into an object. Thinking more generally, we want to use a JSON object as a blueprint to construct a new instance of the ExampleNocabChild object. What’s needed is a constructor that takes in a JsonObject and extracts the data.

public class ExampleNocabChild {

    public int data;

    public ExampleNocabChild(int data) { this.data = data; }

    public ExampleNocabChild(JsonObject jo) {
        // TODO Validate the JO before loading

        // Load the data from the JO into this class
        this.data = jo["data"];
    }
    
    public JsonObject toJson() {
            JsonObject result = new JsonObject();

            // Convert/ add this class’s data into a JO
            result["data"] = data;

        return result;
    }
}

The TODO is ignored in this example but is incredibly important. There is no guarantee that the provided JsonObject has a field called “data”. This is one of the major negatives of using JSON as the data storage tool: It's quite easy for data to become corrupted which can cause the loaded object to become invalid, and in the worst case crash the program, and in extreme cases introduces an attack vector for hackers. The best way to mitigate this risk depends on the context, object being loaded, laziness of the developer and many other important factors. Always validate the data before using it even if you wrote the data yourself.

Update the NocabTester class and valadate the new constructor works and data is correctly extracted from the JSON object. The number 10 should be printed to the debug log.

public class NocabTester : MonoBehaviour {

    private void Start() {
        ExampleNocabChild testChildA = new ExampleNocabChild(10);
        ExampleNocabChild testChildB = new ExampleNocabChild(testChildA.toJson());

        Debug.Log(testChildB.data);
    }
}


This example is simple. Increasing the complexity of the data that is being JSON-ified should be considered on a case by case basis, and the principles for data serialization still apply here. Future JSON tutorials on this website will focus on increasing the complexity of the class hierarchy, namelay JSON-ifying abstract classes and adding interfaces.


Code Dump:

public class ExampleNocabChild {

    private int data;

    public ExampleNocabChild(int data) : base() { this.data = data; }

    public ExampleNocabChild(JsonObject jo) {
        // TODO Validate the JO before loading

        // Load the data from the JO into this class
        this.data = jo["data"];
    }
    
    public JsonObject toJson() {
            JsonObject result = new JsonObject();

            // Convert/ add this class’s data into a JO
            result["data"] = data;

        return result;
    }
}
public class NocabTester : MonoBehaviour {

    private void Start() {
        ExampleNocabChild testChildA = new ExampleNocabChild(10);
        ExampleNocabChild testChildB = new ExampleNocabChild(testChildA.toJson());

        Debug.Log(testChildB.data);
    }
}