1

I am creating a tool that helps the artist in my studio design their UI easily, and then give them the ability to export the UI to the developers to use in their projects.

Long story short, I followed this tutorial: What is the best way to save game state?

and it worked correctly when I wrote everything in the same script.

However when I opened another unity project to import the data, it stopped working correctly. It would read the file, but then the spawnObject variable which is supposed to be instantiated stays null.

The class I use here is called UIdata, and it has only one gameobject variable which is inputObject, and the class is exactly the same as the one I am exporting from the other project

I am not quite sure why it is not working, can deserialization work if its importing something from another project?

Here is my code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Runtime.Serialization;
using System.Xml.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System;
public class DataSaver
{
    public static void saveData<T>(T dataToSave, string dataFileName)
    {
        string tempPath = Application.streamingAssetsPath + "/newUICorrect.txt";
        string jsonData = JsonUtility.ToJson(dataToSave, true);
        byte[] jsonByte = Encoding.ASCII.GetBytes(jsonData);

        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
        }

        try
        {
            File.WriteAllBytes(tempPath, jsonByte);
            Debug.Log("Saved Data to: " + tempPath.Replace("/", "\\"));
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To PlayerInfo Data to: " + tempPath.Replace("/", "\\"));
            Debug.LogWarning("Error: " + e.Message);
        }
    }

    public static T loadData<T>(string dataFileName)
    {
        string tempPath = Application.streamingAssetsPath + "/newUICorrect.txt";
        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Debug.LogWarning("Directory does not exist");
            return default(T);
        }

        if (!File.Exists(tempPath))
        {
            Debug.Log("File does not exist");
            return default(T);
        }
        else
        {
            Debug.Log("found file");
        }
        byte[] jsonByte = null;
        try
        {
            jsonByte = File.ReadAllBytes(tempPath);
            Debug.Log("Loaded Data from: " + tempPath.Replace("/", "\\"));
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To Load Data from: " + tempPath.Replace("/", "\\"));
            Debug.LogWarning("Error: " + e.Message);
        }

        string jsonData = Encoding.ASCII.GetString(jsonByte);

        object resultValue = JsonUtility.FromJson<T>(jsonData);
        return (T)Convert.ChangeType(resultValue, typeof(T));
    }
}

[System.Serializable]
public class UIData
{
    public GameObject inputObject;
}

public class LoadingScript : MonoBehaviour
{
    private GameObject objectToSpawn;
    void Start()
    {

    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.I))
        {
            importObject();
        }
    }
    public void importObject()
    {
        UIData loadedData = new UIData();
        loadedData = DataSaver.loadData<UIData>("UI");
        objectToSpawn = loadedData.inputObject;


        if (loadedData == null)
        {
            return;
        }
        print(objectToSpawn);
    }
}
derHugo
  • 83,094
  • 9
  • 75
  • 115
  • 1
    `inputObject` variable is a type of GameObject and you [cannot](https://stackoverflow.com/a/36869033/3785314) serialize GameObject. You can add information and about the classes that is attached to your GameObject in the UIData class then serialize them – Programmer Oct 08 '17 at 01:40
  • You are sure about that, because I got it working when I serialized an object and deserialized it on another script, but for some reason it is not working on another project. Not to mention, that the example I posted above talks about the possibility of serializing an object I believe – Sam Basiony Oct 08 '17 at 04:39
  • You cannot serialize a GameObject. `inputObject` is a GameObject... – Programmer Oct 08 '17 at 05:08

1 Answers1

0

@Programmer is correct, you cannot serialize engine types. There are several ways to go about resolving your issue. The first is to rebuild your gameobject by serializing its MonoBehaviour components in the following manner (pseudocode):

public GameObject TestSaveAndLoadComponent(MyMonoBehaviour myComponent, GameObject objectToLoadComponentOnto){
        // convert component to Json
        string jsonString = JsonUtility.ToJson(myComponent);

        // Pretend to save the json string to File
        string directory = "TestDirectory";
        string file = "objectFile";
        SaveLoadData.SaveString(directory,file,objectJson);

        // load string from file
        string loadString = SaveLoadData.LoadString(directory,file);

        // add a MonoBehaviour component to the object so that it can be
        // overwritten by the JsonUtility
        MyMonoBehaviour componentToOverwrite = objectToLoadComponentOnto.AddComponent<MyMonoBehaviour>();
        JsonUtility.FromJsonOverwrite(loadString, componentToOverwrite);

        return newObject;
}

JsonUtility may not provide a deep copy of your your monoBehaviour class, I don't use it much. It's pretty bare bones, and if it doesn't meet your needs, there's always Json.Net for unity.

All that being said, you shouldn't need to serialize gameObjects or monobehaviours if you structure your code in the proper way, using an Model-view-controller pattern. In such a case you would only need to serialize very simple classes that define the Model for the UI, and then the your view classes would rebuild the ui in a separate scene or project from the given model.

  • Well the thing is I am not using models, I am using just ui image gameobject. and also, maybe I am miss understanding you, which I apologize if I am. But I already got it working using the method above when serializing the gameobject and deserializing it in the same project. It only stops working when serializing the gameobject and deserializing in a separate project despite using the same code. – Sam Basiony Oct 08 '17 at 05:55
  • The 'print(objectToSpawn);' in your ImportObject function does not return null? – Michael Curtiss Oct 08 '17 at 06:02
  • I suspect what is happening is that in your original project, you aren't using the 'saved' gameObject from the file, you are using the reference to the object in the scene or some other place in that project. When you are moving to a different project, you are trying to actually use the saved gameobject, and it isn't working because you cant serialize gameobjects. Try serializing and deserializing a gameobject field in a very simple test. I've just done it, and in my Json save file it just reads: "null" – Michael Curtiss Oct 08 '17 at 06:10
  • So for those projects I used the exact same code, the only difference was for the first code I wrote at the end: gameobject afterImport = loadeddata.inputobject. And it succeeded and it even showed in the editor that the object is no longer null. For the second project, the only line I changed was objectToSpawn = loadeddata.inputobject. print(objecttoSpawn). It is true that I load the gameobject at the beginning as a reference in order to serialize it. but that is it, I am not touching the input object at all and the code is the same. – Sam Basiony Oct 08 '17 at 06:14
  • OH sorry. My bad, was looking at the wrong file. My test save reads: {"inputObject":{"instanceID":0}} So it isn't serializing the gameobject itself, JsonUtility is serializing the ID reference tag to the game object in the scene. This would explain why it works in one project but not the other. JsonUtility is just trying to assign the scene reference on deseriailization, and when it doesn't find it, the output is null. Learned something new today :). Hopefully it all makes sense now. – Michael Curtiss Oct 08 '17 at 06:18
  • oh that makes a lot of sense, and as a beginner with serialization. I learnt something new myself as well. So what can I do now? how can I serialize a gameobject and deserialize it in another project? – Sam Basiony Oct 08 '17 at 06:21
  • If your components contain references to other gameobjects or components in the scene, which they probably do, then my original solution probably wont work out, because serializing references is difficult. In which case you would want to see about structuring your code in such a way that you can just serialize a Model, which doesn't contain references to any objects in the scene so it makes it easy to serialize. What may relieve all of your issues is to take a look at the PrefabUtility class in the API. Maybe you can save a prefab and share it between projects. – Michael Curtiss Oct 08 '17 at 06:26
  • I thought about that, but as far as I am aware there is no way to save a prefab during runtime and exporting it to let's say streamingassets, so that it can be used in another project. is there? – Sam Basiony Oct 08 '17 at 06:28
  • In a build, no, but in the unity editor, PrefabUtility.CreatePrefab can be used during runtime. – Michael Curtiss Oct 08 '17 at 06:34
  • how would I import it during runtime in another project by code? – Sam Basiony Oct 08 '17 at 06:36
  • The CreatePrefab function just saves it to a file on the machine. Its a *.prefab file, so maybe just copy that file and move it into a folder in the current project? You would then have to hook it up in your scene manually, but maybe if you overwrite a prefab file the reference in the scene will be preserved so you only have to do it once.= – Michael Curtiss Oct 08 '17 at 06:41