- Home /
Best method to save a multi-dimensional array as a file?
Example;
bool[,,] data = new bool[4,4,4];
I'm having trouble coming up with a way (or -the- way) of saving something such as the above 3D array to a file. A crude way would be reading and writing from a .txt file where a line of text represents two dimensions;
1001#0000#1110#0101
And the different lines represent the final dimension;
1001#0000#1111#0101
1111#...
1000#...
0100#...
If that makes any sense... but I feel like there MUST be an easier way to write/read this kind of data to a file. Like turning it into raw byte data and saving it? Is that possible? I'm not asking for an exact code/method (unless you feel generous), but simply some guidance towards articles where I can learn for myself. Could anyone help point me in the right path towards figuring out how to do the following effectively?
a) Read/Write file data for a 3D array of boolean's
b) Read/Write file data for a 3D array of Color32's
(C# language preferred)
This answers my exact need! Thank you. If it would not be too much trouble, could I see the deserialize code as well? From there I can sculpt it to my need :) You are a very big help.
I converted my comment to an answer and will post the DS code in a comment under it.
Answer by Cherno · Dec 23, 2014 at 02:10 AM
The general method of making a multi-dimensional array "flat" should be obvious:
bool[] flatBoolArray = new bool[myMultiArray.GetLength(0) * myMultiArray.GetLength(1) * myMultiArray.GetLength(2) * myMultiArray.GetLength(3)];
int i = 0;
for(int a = 0; a < myMultiArray.GetLength(0); a ++) {
for(int b = 0; b < myMultiArray.GetLength(1); b ++) {
for(int c = 0; c < myMultiArray.GetLength(2); c ++) {
for(int d = 0; d < myMultiArray.GetLength(3); d ++) {
flatBoolArray[i] = myMultiArray[a,b,c,d];
i ++;
}
}
}
}
Can also be done with a flat list, of course.
About actually writing it to file: You could use a BinaryFormatter for that to serialize your data and save it in a file.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public static class SaveLoad {
public static Game loadedGame = new Game();
public static void Save(Game saveGame) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create ("C:/Savegames/" + saveGame.savegameName + ".sav"); //you can call it anything you want
bf.Serialize(file, saveGame);
file.Close();
Debug.Log("Saved Game: " + saveGame.savegameName);
}
}
Now, the Save function takes a Game variable, and this is simpl a custom class that is marked as [System.Serializable] and has variables for the saved game name (string), as well as any other kind of data that you want to save (no types particular to Unity are allowed, such as Transforms, Vector3s, GameObjects, however. You need workarounds for that, for example a Vector3 can be represented as three floats instead. Anyway, that is basically all there is to it. In your case, the Game class would have a bool[] variable that holds your flattened md array, which can be serialized without a hitch. Deseializing on game loading is a little bit trickier but if you need help with that I can give you the code as well.
P.S.: a Game class can also hold any kind of your custom classes or lists & arrays thereof, bas long as these are marked as [System.Serializable] as well and for them, the same limitations apply (no Unity types... If they have these, the relevant fields need to be marked as [System.NonSerialized) and would need workaround, too)
P.P.S: The advantage of serializing like this is that the resulting file is basically encrypted and can't be read without using the deserializing function in your game. It's also MUCH faster than writing strings to a txt file :)
Here is the code for Deserializing a saved game again:
public static void Load(string gameToLoad) {
if(File.Exists("C:/Savegames/" + gameToLoad + ".sav")) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open("C:Savegames/" + gameToLoad + ".sav", File$$anonymous$$ode.Open);
loadedGame = (Game)bf.Deserialize(file);
file.Close();
Debug.Log("Loaded Game: " + loadedGame.savegameName);
}
}
This belongs into the SaveLoad script from the answer above because it uses the loadedGame variable. Once this variable hold a reference to our deserialized Game variable, we can then access this variable as usual. Note that for my own project, I used an array with only three dimensions, so you need to modify the code to correctly increase the iteration variables :) $$anonymous$$y tip is to take a piece of paper and a pen and write the whole loop system down and then think how it works and how a fourth dimension can be added. Shouldn't be too hard with the code below.
//ClearScene();//If you want, you can call a function like this that just checks each gameobject in the scene wether it is somehow marked as "please don't destroy me if a game is loaded", like a game manager would, or a player object or something. All objects that need to persists. Of course, this would be the same as when switching scenes.
SaveLoad.Load("blubb");//call the Load function and pass it a the name of the saved game file to load.
Game loadedGame = SaveLoad.loadedGame;//cache our loadedGame variable from the SavedLoad static class
int a = 0;
int b = 0;
int c = 0;
for(int i = 0; i < loadedGame.flatBoolArray.Length; i ++) {
my$$anonymous$$ultiArray[a,b,c] = loadedGame.flatBoolArray[i];
c++;
if (c == my$$anonymous$$ultiArray.GetLength(2)) {
c = 0;
if(b < my$$anonymous$$ultiArray.GetLength(1) - 1) {
b++;
}
else {
a++;
b = 0;
}
}
}
$$anonymous$$any thanks again! This will go to good use. $$anonymous$$arked as correct answer as well.
$$anonymous$$y pleasure. It took me a long time to get the loop right so it's good that someone else can use it :D
Oh, I jsut noticed that you actually used a three-dimensional array in your example as well.. I somehow assumed you used a fourth dimension :D In that case, the DS code needs no modification, and in the answer, the outermost or innermost cycle can be deleted and the flat array's length isn't multiplied by it as well.
I also recommend taking a look at the Uniter Serializer, a free asset available on the asset store. Very comprehensive and powerful, although for complex projects it's possible that it breaks down and gives errors, and it gets no further updates but hey it's free and it is a great source of information about advanced serializing.
I've followed this and it's been super helpful, however when I try to deserialize it throws me a null error on my array. It reads the loaded array in fine but that loop you have at the end doesn't work for me :(
for(int i = 0; i < loadedGame.flatBlockArray.Length; i++)
{
blocks[a,b,c] = loadedGame.flatBlockArray[i];
c++;
if (c == blocks.GetLength(2))
{
c = 0;
if(b < blocks.GetLength(1) - 1)
{
b++;
}
else
{
a++;
b = 0;
}
}
}
blocks is null and it throws this error: NullReferenceException: A null value was found where an object instance was required.