- Home /
Unity3D: How to save an array of gameobjects with arrays of position in xml and load them back in correctly?
Hello everyone! I don´t know how this question system works. I am relativly new to unity and even newer to serialization and deserialization. I just picked it up 3 days ago. Now I am stuck. Here is a complete rundown of the game part that needs to be saved. I created a game, where the player starts on an instantiated field of objects. The player can then built new objects by left-clicking and destroy objects by right-clicking. Because this is a map creator it´s a good idea to be able to save the map. So i had to get a save system. I had to realise quickly, that there are no tutorials that fit my needs. So i only had 2 options: either give up or try to create my own saving system. I did the second one. And it works!. . . kind of. Not really. Basicly i got four classes: 1 that stores the data, that is going to be serialized, 1 that contains all the data, that is going to be transferred to the first class, 1 that just contains the amount of objects and 1 that gives every object an ID. Right now i only store the Vector3 position as 3 floats, the IDs of the gameobjects and the number of gameobjects (you are going to see everything in this question). When pressing M the Save function gets called, when L is pressed, the Load function is called.
.
//Class 1: This is the one contains the data, that is saved and loaded
.
[System.Serializable]
public class XMLSerialize {
public float[] PosX;
public float[] PosY;
public float[] PosZ;
public int[] BlockID;
public int AmountOfBlocks;
}
//Class 2: This one just contains the amount of blocks and might be a little overkill.
.
public class AmounOfBlocks : MonoBehaviour {
public int AmountOfBlocks;
}
//Class 3: This one is responible for getting the custom ID of every Block in the scene. It sits on every object, that need to be saved.
.
public class GetInstanceId : MonoBehaviour {
public AmounOfBlocks amounOfBlocks;
public GameObject thisObject;
public int elementId;
void Start()
{
thisObject = this.gameObject;
amounOfBlocks = AmounOfBlocks.FindObjectOfType<AmounOfBlocks>();
amounOfBlocks.AmountOfBlocks++;
elementId = amounOfBlocks.AmountOfBlocks;
}
}
.
Class 4: This is the big one: it is responsible for Saving, Loading and converting the data to and from XMLserialize
.
[System.Serializable]
public class XMLManager : MonoBehaviour {
public XMLSerialize xmlSerialize;
public AmounOfBlocks AOB;
public GetInstanceId[] getInstance;
public static XMLManager xmlManager;
public int AmountOfBlocks;
public int[] InsatnceID;
public float[] PosX;
public float[] PosY;
public float[] PosZ;
public GameObject[] reprGameObject;
public bool saving = false;
void Awake()
{
xmlManager = this;
}
public void Update()
{
AOB = AmounOfBlocks.FindObjectOfType<AmounOfBlocks>();
getInstance = GetInstanceId.FindObjectsOfType<GetInstanceId>();
AmountOfBlocks = AOB.AmountOfBlocks;
PosX = new float[AmountOfBlocks];
PosY = new float[AmountOfBlocks];
PosZ = new float[AmountOfBlocks];
xmlSerialize.PosX = new float[AmountOfBlocks];
xmlSerialize.PosY = new float[AmountOfBlocks];
xmlSerialize.PosZ = new float[AmountOfBlocks];
xmlSerialize.BlockID = new int[AmountOfBlocks];
reprGameObject = new GameObject[AmountOfBlocks];
InsatnceID = new int[AmountOfBlocks];
for (int i = 0; i < AmountOfBlocks; i++)
{
InsatnceID[getInstance[i].elementId - 1] = getInstance[i].elementId;
reprGameObject[getInstance[i].elementId - 1] = getInstance[i].thisObject;
xmlSerialize.AmountOfBlocks = AmountOfBlocks;
if (reprGameObject[getInstance[i].elementId - 1] != null)
{
PosX[getInstance[i].elementId - 1] = reprGameObject[getInstance[i].elementId - 1].transform.position.x;
PosY[getInstance[i].elementId - 1] = reprGameObject[getInstance[i].elementId - 1].transform.position.y;
PosZ[getInstance[i].elementId - 1] = reprGameObject[getInstance[i].elementId - 1].transform.position.z;
if (saving == true)
{
xmlSerialize.PosX[getInstance[i].elementId - 1] = PosX[getInstance[i].elementId - 1];
xmlSerialize.PosY[getInstance[i].elementId - 1] = PosY[getInstance[i].elementId - 1];
xmlSerialize.PosZ[getInstance[i].elementId - 1] = PosZ[getInstance[i].elementId - 1];
xmlSerialize.BlockID[getInstance[i].elementId - 1] = InsatnceID[getInstance[i].elementId - 1];
}
}
}
if (Input.GetKeyDown(KeyCode.M))
{
SaveData();
}
if (Input.GetKeyDown(KeyCode.L))
{
LoadData();
for(int i = 0; i < xmlSerialize.AmountOfBlocks; i++)
{
PosX[getInstance[i].elementId - 1] = xmlSerialize.PosX[getInstance[i].elementId - 1];
PosY[getInstance[i].elementId - 1] = xmlSerialize.PosY[getInstance[i].elementId - 1];
PosZ[getInstance[i].elementId - 1] = xmlSerialize.PosZ[getInstance[i].elementId - 1];
InsatnceID[getInstance[i].elementId - 1] = xmlSerialize.BlockID[getInstance[i].elementId - 1];
Instantiate(reprGameObject[InsatnceID[i]], new Vector3(PosX[getInstance[i].elementId - 1], PosY[getInstance[i].elementId - 1], PosZ[getInstance[i].elementId - 1]), Quaternion.identity);
}
saving = false;
}
}
public void SaveData()
{
XmlSerializer serializer = new XmlSerializer(typeof(XMLSerialize));
FileStream stream = new FileStream(Application.dataPath + "/Saves/levelsave.xml", FileMode.Create);
serializer.Serialize(stream, xmlSerialize);
stream.Close();
saving = true;
}
public void LoadData()
{
if (File.Exists(Application.dataPath + "/Saves/levelsave.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(XMLSerialize));
FileStream stream = new FileStream(Application.dataPath + "/Saves/levelsave.xml", FileMode.Open);
xmlSerialize = serializer.Deserialize(stream) as XMLSerialize;
stream.Close();
}
}
}
.
There are a couple of problems in my code, none of them are compiler errors, as you can see. But i might have goofed somewhere with the arrays. I just can´t detect the problem. Whenever the player destroys a block of the original platform, the index of the array is too large. Whenever i load in the save, the index of the array is to large. Sorry if my english isn´t the greatest and the formatting of this question is terrible, but it´s the first time i wrote a question on this forum. I am really bad at explaining my problem, so if anything is mispelled or unclear, feel free to ask me. Thanks in advance.
with a quick look and if undrestood correct I believe you should put the code in Update above the if (Input.GetKeyDown(KeyCode.L)) into it and before SaveData() because it is executed in every frame
Thanks for answering, but that is not the problem. The problem is within the arrays itself somewhere, as I always get "index out of array" errors. I just can´t discover where the problem begins and why it did. Nonetheless thanks for your answer!