What I am specifically asking for is a way to store that data in any file format without needing to massively rework my game.
It should be noted I am a novice level programmer and in way over my head, so detail would be appreciated.
No offense, but the clumsy way you're storing your data is just making things worse for you. You should seriously consider changing it, even if that means you need to rework some of your game. If you start from a bad foundation and just keep building on that bad foundation, you're just going to end up with code that is more and more of a mess until it gets to the point that it's impossible to make any progress. This often means giving up on the project and sometimes giving up on game development altogether.
In the engine, your data should probably be stored more along the lines of something like this:
[System.Serializable]
public struct Cell {
[SerializeField] private int x;
[SerializeField] private int y;
[SerializeField] private BuildingInfo buildingInfo;
[SerializeField] private int ammo;
public int X{ get => x; set => x = value; }
public int Y{ get => y; set => y = value; }
public Vector2Int Position => new Vector2Int(x, y);
public BuildingInfo BuildingInfo { get => buildingInfo; set => buildingInfo = value; }
public int Ammo { get => ammo; set => ammo = value; }
//etc
}
//if most cells will have data, we can consider a 2D table:
private Cell[,] cells = new Cell[2000, 2000];
//to retrieve a cell
var cell = cells[x,y];
//or if most cells will not have data, we should not waste an enormous amount of
//space populating a 2D table with empty cells
private Dictionary<Vector2Int, Cell> cells = new Dictionary<Vector2Int, Cell>(100);
//to add a cell:
var cell = new Cell();
cell.X = x;
cell.Y = y;
//etc
cells.Add(cell.Position, cell);
//to retrieve a cell
if (cells.TryGetValue(new Vector2Int(x, y), out var cell) {
//do something with the cell here
}
//to loop through all cells
foreach (var cell in cells.Values) {
//do something with the cell here
}
Because the struct in my example is serializable, you can put the structs into a ScriptableObject:
[CreateAssetMenu(menuName = "Saved Map")]
public class SavedMap : ScriptableObject {
[SerializeField] private List<Cell> cells = new List<Cell>(100);
public List<Cell> Cells { get => cells; set => cells = value; }
public void SaveFromDictionary(Dictionary<Vector2Int, Cell> dict) {
cells.Clear();
foreach (var cell in dict.Values) {
cells.Add(cell);
}
//special code that only runs in the Editor to make sure the Editor recognizes that the data changed and needs to be saved to disk
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
}
//this is very expensive, only call it once when you start the game and save the result to a variable
public Dictionary<Vector2Int, Cell> ToDictionary() {
Dictionary<Vector2Int, Cell> dict = new Dictionary<Vector2Int, Cell>(cells.Count);
foreach (var cell in cells) {
dict[cell.Position] = cell;
}
return dict;
}
}
public class MapLoader : MonoBehaviour {
[SerializeField] private SavedMap map;
private Dictionary<Vector2Int, Cell> cells;
private void Awake() {
Assert.IsNotNull(map);
cells = map.ToDictionary();
}
public Dictionary<Vector2Int, Cell> Cells => cells;
}
A ScriptableObject class defines a type of asset file that you can create, edit, and save in the Unity Editor. You can edit a ScriptableObject asset in the Inspector in the same way you edit any GameObject. In this case manually editing the cells in the Inspector would be a nightmare, so you'd probably want to have a script pass the data into the ScriptableObject to save.
If you don't know how to use Dictionaries or ScriptableObjects or anything else I've mentioned, please do a web search and read the official documentation. This is the best way to learn!
You might also want to eventually make a custom Editor
or EditorWindow
script for easily editing a SavedMap in the Unity Editor.