4
\$\begingroup\$

I'm currently creating an external tool to generate dialogues that are exported as JSON files.

The idea is that after you create a dialogue (as a JSON), my main Unity project reads that file and generates a dialogue.

Right now it works just fine, but the JSON is an external file that you can modify even after compiling the game (of course).

Is there a way to actually "hardcode" that JSON file so that the game doesn't need to find and read the file every single time it plays?

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Have you considered storing your dialogue as ScriptableObjects instead? You can still use your JSON parsing to populate the objects, but just so it at edit/build time instead of at runtime. \$\endgroup\$ – DMGregory Jul 10 '19 at 20:53
  • \$\begingroup\$ thanks, this is exactly what I would like to do, but then how do I do that at edit/build time? thank you \$\endgroup\$ – roseicollis Jul 11 '19 at 9:25
  • 1
    \$\begingroup\$ It's not clear what is the issue you're trying to solve? Do you want to hide the content from the user, or do you just want to have the content part of the binary to potentially load the game faster? \$\endgroup\$ – Vaillancourt Jul 11 '19 at 12:13
2
\$\begingroup\$

Let's assume somewhere in your code you do something like this to open the external JSON file, read its text, and parse it into a structured Dialogue object representation in memory.

DialogueData dialogue = DialogueUtility.ParseJSONFromPath(filepath);

Since you already have the file I/O, parsing, and validation covered above, I'll elide those parts in this answer, and just focus on how we can cache the result of this call at edit time rather than doing it at runtime.

First we define a new asset type that can live in our project's Assets/ folder:

using UnityEngine;

// This line makes the asset show up in the Create Asset menus:
[CreateAssetMenu(fileName = "NewDialogue.asset", menuName = "Custom Data/Dialogue")]
public class DialogueAsset : ScriptableObject {

    // Our asset is just a container for your existing dialogue data.
    // This type must be [Serializable] or a Unity type to save correctly.
    public DialogueData dialogue;

    // The remaining parts we'll do only in-editor, and strip from the built game:
#if UNITY_EDITOR
    // Provide a path to the source JSON file. 
    public string sourcePath;

    // Populate the data from the source file.
    // (Assuming DialogueUtility handles any errors itself)
    public void Import() {
        dialogue = DialogueUtility.ParseJSONFromPath(sourcePath);
    }
#endif
}

Now inside a folder called Editor/, we'll define a custom editor for this type:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(DialogueAsset))]
public class DialogueAssetEditor : Editor {

    public override void OnInspectorGUI()
    {
        // Show default inspector property editor, 
        // so we don't need to draw the DialogueData field ourselves.
        DrawDefaultInspector ();

        // Get a typed reference to the DialogueAsset we're editing.
        var asset = (DialogueAsset)target;

        // Add a button we can click to fire the "Import" method.
        if(GUILayout.Button(asset.dialogue == null ? "Import" : "Reimport") {
            // Log the change we're about to make so it's saved/undoable correctly.
            Undo.RecordObject(asset, "Import JSON");

            // Import the data.
            asset.Import();
        }
    }
}

Now you can create a new DialogueAsset by right-clicking in your project window and selecting Create --> Custom Data --> Dialogue. You can put the path to the source file in its sourcePath field, then click the "Import" button to do your JSON parsing and store the deserialized result into the asset.

The rest of the game can just reference this asset to read out its pre-digested DialogueData, without keeping the original raw JSON in your released game or parsing it at runtime. We're piggybacking on the Unity serializer to encode this data into our asset files and deserialize it when we run the game, and it does this in binary rather than text so it should be smaller, faster, and more resistant to unwanted tampering (though you should still assume that any data in the game client can be read and modified by a sufficiently motivated individual, and not count on this for security).

Note that in order for this to save and survive into your running game, your DialogueData has to be something that Unity's serializer can understand and reproduce faithfully. Unity types (including your own custom ScriptableObject variants) will work, as will [System.Serializable] types as long as you're not relying on polymorphism or circular references between types.

If you need help adapting your DialogueData to something Unity can serialize, please feel free to post that as its own question.

\$\endgroup\$
1
\$\begingroup\$

Nothing is keeping you from encrypting the assets included in your game.

\$\endgroup\$
1
  • \$\begingroup\$ Even though a simple answer, I'd say it's correct. Even if you include the JSON files inside binaries, there's still a chance that someone can edit that and search for human-readable strings, and alter them. Encrypting the files in some way (doesn't have to be the most secure) would prevent most casual users from altering these files. \$\endgroup\$ – TomTsagk Jul 11 '19 at 9:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.