- Home /
Saving/Loading Multiple Buildings With Binary Formatter
Hello! I recently watched a video on youtube regarding saving and loading with the use of a binary formatter. My goal is to make a resource management - colony building game, and i want to give the player the ability to save and load all of his buildings at the positions that he placed them. The way i have the system currently works fine, although, it only saves one building at a time. Is there a way to make it so it saves all of the placed buildings? I have provided the system's code, so, if you can help, i would appreciate it, because i have been trying to fix it for hours. Code:
Save System:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
public static class SaveSystem
{
public static void Save(BuildingScript B)
{
BinaryFormatter formattter = new BinaryFormatter();
string path = Application.persistentDataPath + "/building.lol";
FileStream stream = new FileStream(path, FileMode.Create);
BuildingData data = new BuildingData(B);
formattter.Serialize(stream, data);
stream.Close();
}
public static BuildingData Load()
{
string path = Application.persistentDataPath + "/building.lol";
if(File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
BuildingData data = formatter.Deserialize(stream) as BuildingData;
stream.Close();
return data;
}
else
{
Debug.LogError("File Not Found");
return null;
}
}
}
Building Data:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class BuildingData
{
public int health;
public int cost;
public float[] position;
public BuildingData(BuildingScript B)
{
health = B.health;
cost = B.cost;
position = new float[3];
position[0] = B.transform.position.x;
position[1] = B.transform.position.y;
position[2] = B.transform.position.z;
}
}
Building Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BuildingScript : MonoBehaviour
{
public int health;
public int cost;
}
Game Manager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public void Save()
{
SaveLoad.Save(GameObject.FindObjectOfType<BuildingScript>());
}
public void Load()
{
BuildingData data = SaveLoad.Load();
GameObject.FindObjectOfType<BuildingScript>().health = data.health;
GameObject.FindObjectOfType<BuildingScript>().cost = data.cost;
Vector3 pos;
pos.x = data.position[0];
pos.y = data.position[1];
pos.z = data.position[2];
GameObject.FindObjectOfType<BuildingScript>().transform.position = pos;
}
}
Answer by SirPaddow · Aug 19, 2019 at 11:25 PM
Your formatter can receive any data that is serializable. When you have a list, you can use an array and it will be Serializable. So you will need another script containing the array, let's say AllBuildingsData:
[Serializable]
public class AllBuildingsData {
public BuildingData[] buildingDatas;
public AllBuildingsData(BuildingData[] buildingDatas) {
this.buildingDatas = buildingDatas;
}
}
Your Save function will then look something like this:
public static void Save(BuildingScript[] buildingScripts) {
BinaryFormatter formattter = new BinaryFormatter();
string path = Application.persistentDataPath + "/building.lol";
FileStream stream = new FileStream(path, FileMode.Create);
BuildingData[] buildingDatas = new BuildingData[buildingScripts.Length];
for (int i = 0; i < buildingScripts.Length; ++i) {
buildingDatas[i] = new BuildingData(buildingScripts[i]);
}
AllBuildingsData data = new AllBuildingsData(buildingDatas);
formattter.Serialize(stream, data);
stream.Close();
}
And in the Load function, you don't get a BuildingData anymore, but a AllBuildingsData:
AllBuildingsData data = formatter.Deserialize(stream) as AllBuildingsData;
Bonus, unrelated to your question, but for better code! You should definitely cache your objects instead of calling 3 times the "FindObjectByType". A call to this function is really expensive, so one call is better!
BuildingScript buildingScript = GameObject.FindObjectOfType<BuildingScript>();
buildingScript .health = data.health;
buildingScript .cost = data.cost;
Hello SirPaddow! Thank you for your help! I appreciate it! However, it seems like i have done something wrong, because the system now stores only the first position value, and applies it to every building in the scene. That is not what i am trying to make. I am trying to make a system that stores each building's position and then loads the correct position for each building.However, the code that you gave me appears to store every position, so maybe i wrote the load function incorrectly? Or maybe the game manager? I will provide you with the code, and if you are available, i would appreciate some help on this task. Code: Save System:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
public static class SaveSystem
{
public static void Save(BuildingScript[] buildingScripts)
{
BinaryFormatter formattter = new BinaryFormatter();
string path = Application.persistentDataPath + "/building.lol";
FileStream stream = new FileStream(path, File$$anonymous$$ode.Create);
BuildingData[] buildingDatas = new BuildingData[buildingScripts.Length];
for (int i = 0; i < buildingScripts.Length; ++i)
{
buildingDatas[i] = new BuildingData(buildingScripts[i]);
}
AllBuildingsData data = new AllBuildingsData(buildingDatas);
Debug.Log(data);
formattter.Serialize(stream, data);
stream.Close();
}
public static AllBuildingsData Load()
{
string path = Application.persistentDataPath + "/building.lol";
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, File$$anonymous$$ode.Open);
AllBuildingsData data = formatter.Deserialize(stream) as AllBuildingsData;
stream.Close();
return data;
}
else
{
Debug.LogError("File Not Found");
return null;
}
}
}
Game $$anonymous$$anager:
public class Game$$anonymous$$anager : $$anonymous$$onoBehaviour
{
public void Save()
{
SaveSystem.Save(GameObject.FindObjectsOfType<BuildingScript>());
}
public void Load()
{
AllBuildingsData data = SaveSystem.Load();
BuildingScript buildingScript = GameObject.FindObjectOfType<BuildingScript>();
for (int i = 0; i < data.buildingDatas.Length; i++)
{
buildingScript.health = data.buildingDatas[i].health;
buildingScript.cost = data.buildingDatas[i].cost;
Vector3 pos;
pos.x = data.buildingDatas[i].position[0];
pos.y = data.buildingDatas[i].position[1];
pos.z = data.buildingDatas[i].position[2];
GameObject.FindObjectOfType<BuildingScript>().transform.position = pos;
}
}
}
You could try and use some human-readable formatter, to see if the problem happens during the save or during the load process
Ok, never$$anonymous$$d, i debugged it myself. The problem was in the load function of the game manager. I figured it out using the suggestion from pangamni. I placed a Debug.Log command in the save and load function, and the problem was that in the game manager, when loading, i only referenced a single gameobject, and not all of them. So, thank you both for your help, you are life savers!
@$$anonymous$$ikaelgameryolo1 Hey could you please upload your updated code, i'm having the same problem trying to load all gameObject position and cant seem to solve it.
Just to be clear, you are trying to load every object in its own position, and everything loads in the position of the first/last object, right?
yes that is correct. How would you referenced all the gameobject in the load function
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Google play games in Javascript? 1 Answer
Making Building Placement Buildings rotateable 4 Answers
how to determine if two objects are occupying the same space? 1 Answer