0
\$\begingroup\$

I create buildings and they have child Text objects that will reflect the building level.

The Problem:

What can I use to store the buildings a player has built so the data can be persisted?

I've looked at Lists, but when I upgrade a building, how would I update the list so that particular building has its new level reflected?

I'm working on a dictionary so I can update the key with the new value (level) but running into a problem with the keys and names. If I have lets say 10 "Farms" and try to add them to the dictionary, even though they have different properties, I get an error that I have a duplicate.

Currently I am learning how to persist that data through scenes;

GameManager.CS

public class GameManager : MonoBehaviour
{

public static Dictionary<Building, int> playerBuilt = new Dictionary<Building, int>();

public void SaveState()
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Open(Application.persistentDataPath + "/EmpireGame", FileMode.Open);

        SaveClass sc = new SaveClass()
        {
            food = Player.food.amount,
            buildingList = playerBuilt
        };

        bf.Serialize(file, sc);
        file.Close();
    }
}

[Serializable]
class SaveClass
    {
        public int food, wood, iron, stone;
        public Dictionary<Building, int> buildingList = new Dictionary<Building, int>();
    }

Building.CS

public abstract class Building : MonoBehaviour, IUpgradable {

    public int ID;

    //level this building is
    public Text levelText;
    public int level = 0;

    //starting cost to amplify the upgrade cost
    public int foodBase, woodBase, ironBase, stoneBase;

    //Upgrade cost
    public int foodCost, woodCost, ironCost, stoneCost;
    public float BuildingCostPercentage = 5f;

    public void UpdateDictionary()
    {
        if (GameManager.playerBuilt.ContainsKey(this))
            GameManager.playerBuilt[this] = level;
        else
            GameManager.playerBuilt.Add(this, level);
    }
}

Note: I have not tested the save function so I expect errors.

\$\endgroup\$
  • \$\begingroup\$ I could try concatenating the name of the gameobject with their world position so they are all unique, but I think when I reload them, their names will visually have their positions appended to them. \$\endgroup\$ – Anon Apr 8 '18 at 13:08
  • \$\begingroup\$ Can you show us a sample of your Building type, so we know how much individual variation they have that you need to save? If it's just a type and a level, it seems like storing a list of building records with a type ID and an integer level would suffice. But maybe you need something more complicated? (eg. do your buildings have positions? Internal state like production orders or resource levels?) \$\endgroup\$ – DMGregory Apr 8 '18 at 13:12
  • \$\begingroup\$ @DMGregory thanks Greg, just updated the post to add the properties of a building. \$\endgroup\$ – Anon Apr 8 '18 at 13:21
2
\$\begingroup\$

Myself, I'd pull out just the parts of the building I really want to save - ie. those that are unique to this instance of the building. Eg..

[System.Serializable]
public struct BuildingRecord {
    public BuildingType type;
    public Vector2 position;
    public int level;
}

Here BuildingType could be an enumeration, or a reference/index to a Type Object, or even just a string key - whatever will help you look up the appropriate source assets like a prefab when you want to spawn this building back into the game when you load from your save file.

Your Building can have a method like so...

public BuildingRecord GetRecord() {
    return new BuildingRecord(){
       type = this.type,
       position = (Vector2)transform.position,
       level = this.level
    };
}

Then your save function can simply iterate over a list of Building component instances that you've maintained all along, or fetched on demand with FindObjectsOfType<Building>() and make a simple serializable array of them:

BuildingRecord[] SaveBuildings(IList<Building> buildings) {
    var records = new BuildingRecord[buildings.Count];
    for(int i = 0; i < records.Length; i++)
        records[i] = buildings[i].GetRecord();
    return records;
}

On deserialization, you can pass each record to a factory method to look up the right prefab using the type information, spawn it at the given position, and initialize it to the given level.

\$\endgroup\$
  • \$\begingroup\$ It looks good, but I'm not sure about what will happen when I upgrade a buildings level though? Will I need to remove the original record and create a new one on every upgrade? \$\endgroup\$ – Anon Apr 8 '18 at 13:43
  • \$\begingroup\$ No, your records only exist when you save. You update them when you generate your save data, rather than keeping them persistent through the whole game. While the game is running and the buildings are living in your scene, the building instances themselves are the authority about their state. When you want to save, you ask them to digest that information into an easily-saved record. \$\endgroup\$ – DMGregory Apr 8 '18 at 13:47
  • \$\begingroup\$ If you really want to keep a running record, you could instead have each building call GameManager.UpdateBuildingRecord(this) whenever it's changed, and the GameManager finds the corresponding index in its record array and overwrites it with building.GetRecord(), but I don't think that's necessary - you'd have two copies of your game state, and the process of capturing the whole state isn't so complex that you gain performance by doing it incrementally. \$\endgroup\$ – DMGregory Apr 8 '18 at 13:51
  • \$\begingroup\$ That actually makes lots of sense. Thank you for always taking the time to thoroughly explain when answering all my questions. Definitely learning a lot from you :) \$\endgroup\$ – Anon Apr 8 '18 at 14:56
  • \$\begingroup\$ I thought it made sense haha but I still can't get my head around this. Each time I create a building, I run "BuildingRecord"? How does the save file get the list of my buildings and all their levels? What should call "SaveBuildings"? \$\endgroup\$ – Anon Apr 11 '18 at 13:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.