Advanced serialization of GameObject

Hello there,

I’m trying to serialize a gameobject of advanced hierarchy, which is created in an editor out of prefabs.

I have written some simple binary serialization code to handle this for me, but sooner than later I realized this topic needed much more from me than I have to give.

I’m gonna provide you guys with my code so you can see what I’ve come up with.

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

public class SaveRobotAsPrefab : MonoBehaviour {

	List<RobotData> listOfDataLoaded = new List<RobotData>();

	public void Save() {
		GameObject go = GameObject.Find("SquarePlatformStandalone");
		SaveRobot(go);
	}

	public void Load() {
		listOfDataLoaded = LoadRobot();
		foreach(RobotData data in listOfDataLoaded) {
			GameObject robot = Instantiate(Resources.Load(data.name)) as GameObject;
			robot.name = data.name;
			if (data.parentName != null)
			{
				robot.transform.parent = GameObject.Find(data.parentName).transform;
			}
			robot.transform.position = new Vector3(data.posX, data.posY, data.posZ);
			robot.transform.rotation = new Quaternion(data.rotX, data.rotY, data.rotZ, data.rotW);
		}
	}

	public void SaveRobot(GameObject obj) {
		Transform[] allChildren = obj.GetComponentsInChildren<Transform>();
		BinaryFormatter bf = new BinaryFormatter();
		FileStream stream = new FileStream(Application.persistentDataPath + "/robot.sav", FileMode.Create);
		Debug.Log(Application.persistentDataPath);
		List<RobotData> listOfObjects = new List<RobotData>();
		foreach (Transform child in allChildren)
		{
			listOfObjects.Add(new RobotData(child.gameObject));
		}
		bf.Serialize(stream, listOfObjects);
		stream.Close();
	}

	public List<RobotData> LoadRobot()
	{
		List<RobotData> list = new List<RobotData>();
		if (File.Exists(Application.persistentDataPath + "/robot.sav")) {
			BinaryFormatter bf = new BinaryFormatter();
			FileStream stream = new FileStream(Application.persistentDataPath + "/robot.sav", FileMode.Open);
			list = bf.Deserialize(stream) as List<RobotData>;
			stream.Close();
			return list;
		}
		return null;
	}
}

[Serializable]
public class RobotData
{
	public string name;
	public float posX;
	public float posY;
	public float posZ;
	public float rotX;
	public float rotY;
	public float rotZ;
	public float rotW;
	public string parentName;

	public bool isChild() {
	return false;
	}

	public RobotData(GameObject robot) {
		name = robot.name;	
		rotX = robot.transform.rotation.x;
		rotY = robot.transform.rotation.y;
		rotZ = robot.transform.rotation.z;
		rotW = robot.transform.rotation.w;
		posX = robot.transform.position.x;
		posY = robot.transform.position.y;
		posZ = robot.transform.position.z;
		if (isChild())
		{
			parentName = robot.transform.parent.name;
		} else {
			parentName = null;
		}
	}
}

As you can see, I was trying to accomplish this task by saving everything and re-initializing, the problem is that the prefab I’m trying to save, is composed of multiple prefabs, which have in their hierarchy children which are not prefabs. Thus, this won’t work, because my code can’t differentiate between prefabs and just children of prefabs which are not prefabs.

Please help me with this topic, because I’m pretty beaten about this, I’m not good programmer, spent many hours programming and days trying to learn how to do this, and I’m still nowhere closer the solution.

I need to be able to save the robot which is created by the player into a file, so that it can be loaded on the next launch of the game.

Any help is very appreciated.

You might want to take a look at my free SerializeHelper asset: https://community.unity.com/t5/Asset-Store/SerializeHelper-Free-save-and-load-utility-De-Serialize-all/td-p/2291865 It saves and loads a gameobject with it's components as long as all the GameObjects in it's hierarchy are a prefab and have a Identifier component. If you need to reconstruct children which aren't prefabs, then you could always save the component data they have and later create an empty GameObject and add the components with the loaded data.

1 Answer

1

Well, first of all you need a reliable way of identifing a prefab root object. The easiest way is to attach some kind of “Part” or “Prefab” script to each prefab root. Now instead of GetComponentsInChildren<Transform>(); you would simply use GetComponentsInChildren<Part>();

Now you only get those gameobjects which represent prefabs.

The second thing you have to handle is the way how you serialize the hierarchy. Just saving the name of the parent might not be that useful. If you use the same prefab more than once you can’t use the name to determine the parent. It’s not clear how the object is actually built. It would be easier if you had a list of “attach points” which you could reference in some way.

A common way is to give each serialized “instance” an ID which you can use to identify a certain instance. In your case it’s the easiest way to iterate the hierarchy in a tree pattern and simply use the index in the List as ID.

If you want to save the state (position / rotation) of childs which are part of a prefab it get’s way more complicated as it’s difficult to determine which child belongs to which prefab and to match those auto-generated childs with the saved data. The best solution for that would be, when the “Part” script had a public Transform array and you preassign all childs to that array which should be saved.

It all depends on how your object is build and what has to be saved.