0
\$\begingroup\$

I have a problem which I'm sure can be abstracted to some larger concept but I don't really have the knowledge to know what to google. I'll just explain what im trying to do and if you can please tell me how to do it, or what I should be doing instead.

I have a game I am adding a save system to, I haven't even got to the saving part and am faltering.

I have added a dataScript which holds all the game data. In this there are 20 flags(bools) that say whether 20 different doors are open. Every door is controlled with a single doorScript.

Now my issue is how do my individual doors access the specific bool that is relevant to them from the dataScript? Normally I would solve this by having an array of bools in the dataScript, and using a public int in the doorScript to choose the correct bool array index. This isn't really workable when the array gets large however as I have to remember which index corresponds to which bool.

I have seen dictionaries as a possible solution but I can't help but feel I'm making a more fundamental mistake and should be doing something completely different.

Thanks

\$\endgroup\$
2
  • \$\begingroup\$ Why not store each door's status with the door itself? \$\endgroup\$
    – Pikalek
    Commented Dec 31, 2022 at 16:19
  • \$\begingroup\$ I simplified the issue a bit for the purpose of the question. I have lots of different flags that I want to use ie. doorFlags, achievmentFlags, different kinds of progressFlag etc. So I need an approach that works for everything. \$\endgroup\$ Commented Dec 31, 2022 at 21:53

1 Answer 1

3
\$\begingroup\$

For saving the state of multiple objects like this, there's a lot of different techniques you can use depending on just how extensible you'd like this to be.

I'd of course recommend storing all of your data in JSON - but how you architect that state management is up to you. If you'd like a single class to hold a collection of all these doors' data, you can assign each door a GUID and have that be the access key for your saved data.

A simple way to do this automatically would be something like

// your door script
[SerializeField] string saveID;

bool isOpen;

void OnValidate
{
    if(string.IsNullOrWhitespace(saveID))
         saveID = Guid.NewGuid().ToString();
}

void SaveData()
{
     doorSaveManager.Open[saveID] = isOpen;
}

void LoadData()
{
   isOpen = doorSaveManager.Open[saveID]
}

You can also opt for a more robust serialized GUID

However, perhaps it may be best to think of the problem as a more holistic level-specific problem, rather than a door-specific problem. If you have a Save system that focuses on saving the state of anything in the entire level, then you can implement an interface for getting save data from any object in the level.

The following pseudo-code is just an example:


    interface ISaveable
    {
       SaveData MyData { get; }
    }
    
    [System.Serializable]
    abstract class SaveableState
    {
        public string Guid {get; private set; }
    }
    
    [System.Serializable]
    class DoorState : SaveableState
    {
        public bool IsOpen;
    }
    
    class Door : Monobehaviour, ISaveable
    {
       DoorSaveData doorState;
    
       SaveableState MyData => doorState;
    
       void Open()
       {
           PlayDoorAnimation(true);
           doorState.IsOpen = true;
       }
    
       void Close()
       {
           PlayDoorAnimation(false);
           doorState.IsOpen = false;
       }
    }
    
    
    class LevelStateSaver: MonoBehaviour
    {
        void SaveLevel()
    {
    
        var allSaveables = FindObjectsOfType<ISaveable>(true);
        var allSaveData = allSaveables.Select(x => x.MyData);
    
        LevelSave dataToSave = new LevelSave(allSaveData);

        // you may need to save each type specifically - im not that familiar with serializing inherited types into a single save file. It also depends on your Json serializer - you can use unity's JSONUtility or Newtonsoft. if you do need to do that, you can do the following:
    
        var doorData = allSaveData.Where(x => x is DoorState);
        var dropData = allSaveData.Where(x => x is DropState);

        LevelSave dataToSave = new LevelSave(doorData, dropData);
    
    }
    
    [System.Serializable]
    class LevelSave
    {
       List<SaveableState> states
       
       public LevelSave(List<SaveableState> states){
           this.states = states;
       }
    
       public Dictionary<string, SaveableState> GetDictionary()
       {
           return states.ToDictionary(x => x.id);
       }
    }
    
    
    // if you have to save on a per-type basis
    [System.Serializable]
    class LevelSave
    {
        LevelSaveGeneric<DoorState> doorSaves;
        LevelSaveGeneric<DropState> dropSaves;

       public LevelSave(List<DoorState> doors, List<DropState> drops){
           this.doorSaves = new(doors);
           this.dropSaves = new(drops);
       }
    
        [System.Serializable]
        class LevelSaveGeneric<T> where T : SaveableState
        {
            List<T> states;
            public LevelStateGeneric(List<T> states)
            {
                this.states = states;
            }
        }
    
    }

```
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Thank you very much. The holistic approach seems to be the guidance I was missing. \$\endgroup\$ Commented Dec 31, 2022 at 21:56
  • \$\begingroup\$ no problem, glad i could help :) luckily for you this is very much a solved problem - im not going to pretend my example there was the best there is. but it may be a good enough start for your needs. good luck! \$\endgroup\$
    – domportera
    Commented Jan 1, 2023 at 7:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .