Unity3D – Save Your In-Play Transform Modifications (2 of 2)

Don’t you hate it when you’ve made a few changes to your game objects but can’t save them? Perhaps you’ve lined up a couple objects perfectly … but when you press STOP, you know you’ll lose all those modifications and have to re-enter them manually.

Well, not anymore!

playNote: This is an advanced tutorial, recommended for those already familiar with the Unity3D game engine.

This post is continued from Unity3D – Save Your In-Play Transform Modifications (1 of 2)

Okay, now that we have a working OnInspectorGUI() with a nice looking “default” Transform Inspector, lets expand on the “magic code” we glossed over in the previous post. We’ll start by adding side-by-side SAVE and LOAD buttons to the GUILayout, along with function calls to SaveData() and LoadData() that we’ll define in a moment.


    public override void OnInspectorGUI()
    {
        Transform t = (Transform)target;

        DrawABetterInspector(t);

        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Save"))
        {
            SaveData(t.gameObject);
        }

        if (GUILayout.Button("Load"))
        {
            LoadData(t.gameObject);
        }
        EditorGUILayout.EndHorizontal();
    }

The way this is going to work is that we’ll save the transform data temporarily, and then retrieve the saved data when we’re ready (once the game has stopped). I’m going to use a text file to store the data because it is easy to debug if there is a problem … and performance and security are not issues since this code will only ever run on our local machine.

A Unique Identifier

First lets add a function to create a filename that will be unique for the Object (allowing us to save and load multiple Transforms at one time). Since every Unity GameObject must also have one (and only one) Transform component, we can use the GetInstanceID() function on the Transform’s base Object for our unique identifier.

    string GetInstanceFileName(GameObject baseObject)
    {
        return System.IO.Path.GetTempPath() + baseObject.name + "_" + baseObject.GetInstanceID() + ".keepTransform.txt";
    }

As an extra clarification step, I’m also using the object’s name for even more debugging ease.

Note that we’re storing the temporary file in the system’s temporary path. The actually location of the directory will vary based on the Operating System. (One of the awesome things about C# is the System functionality that comes along with it. Unfortunately, not all System functionality is available to non-Windows OS’s. However, there are plenty of less elegant ways of creating a temp directory if this function doesn’t work with your particular OS .. but, I’m sure it’ll be fine.)

Saving Translation, Rotation, Scale

No, lets write this all to the text file as an array of individual lines. This’ll ensure clear and easy reading of the data (both visually and when we import it.)

    public void SaveData(GameObject baseObject)
    {
        List<string> saveData = new List<string>();

        saveData.Add(this.GetInstanceID().ToString());

        saveData.Add(baseObject.transform.localPosition.x.ToString());
        saveData.Add(baseObject.transform.localPosition.y.ToString());
        saveData.Add(baseObject.transform.localPosition.z.ToString());

        saveData.Add(baseObject.transform.localRotation.eulerAngles.x.ToString());
        saveData.Add(baseObject.transform.localRotation.eulerAngles.y.ToString());
        saveData.Add(baseObject.transform.localRotation.eulerAngles.z.ToString());

        saveData.Add(baseObject.transform.localScale.x.ToString());
        saveData.Add(baseObject.transform.localScale.y.ToString());
        saveData.Add(baseObject.transform.localScale.z.ToString());


        System.IO.File.WriteAllLines(GetInstanceFileName(baseObject), saveData.ToArray());
    }

Loading the Transform Data

Finally, we need to load the data back into the object, including converting the text data back to two Vector3s and a Quaternion. As a last step we’ll delete the temporary file to prevent any confusion later.

    public void LoadData(GameObject baseObject)
    {
        string[] lines = System.IO.File.ReadAllLines(GetInstanceFileName(baseObject));
        if (lines.Length > 0)
        {
            baseObject.transform.localPosition = new Vector3(System.Convert.ToSingle(lines[1]), System.Convert.ToSingle(lines[2]), System.Convert.ToSingle(lines[3]));
            baseObject.transform.localRotation = Quaternion.Euler(System.Convert.ToSingle(lines[4]), System.Convert.ToSingle(lines[5]), System.Convert.ToSingle(lines[6]));
            baseObject.transform.localScale = new Vector3(System.Convert.ToSingle(lines[7]), System.Convert.ToSingle(lines[8]), System.Convert.ToSingle(lines[9]));
            System.IO.File.Delete(GetInstanceFileName(baseObject));
        }
    }

Okay, that’s it… we’re all done. Give it a try.

Better yet, you can apply this same concept to your own components. You can have a save/load functionality for anything … a great addition to the tools you’ve already developed for your game.

2D Graphics Programming for Games [book] ThumbnailFor more topics on 2D graphics programming, be sure to check out my book, 2D Graphics Programming for Games available now both online and through your favorite independent bookseller.

Leave a Reply