Not saving Equipment Slots between scenes

Nearly everthing is functioning correctly at this point, except Equipment. For some reason it doesn’t save between scenes. Inventory transfers between scenes and everything workes perfectly. I can equip items in both scenes and unequip them, but the moment I change scene with items in equipment slots they disappear.

Are you getting any error messages when you transition between scenes?

With the code for capturing/restoring already within Equipment.cs, it seems unlikely that the Equipment isn’t getting saved/restored, but there are a couple of possibilities that come to mind:

  • Make sure that there is only ONE StatsEquipment on the Player. Multiple StatsEquipments would cause a blank record from the second StatsEquipment to overwrite the record from the first one.
  • Make sure that both scene’s players have a StatsEquipment, but not an Equipment. A mismatch would cause the data to be stored under different indexes, StatsEquipment doesn’t “match” Equipment.
  • Make sure all of your items are in a Resource Directory and that they have a valid unique ID.

There are no errors. Every item is in Resources Directory and have unique ID. Every player also has only one StatsEquipment. I think that the problem is in how I transition between scenes. I’m creating something more like x-com so I don’t have any portal objects on map. At the moment most of my transition happen by pressing button to change scene.

So other things would also not be saving, such as experience (level), and current health if that were the case.

Paste in the script you are using to transition between scenes. Is it tied to a GameObject (vital for a coroutine to work)? Walk me through the way you change scenes.

It’s honestly basic listening if specific button is pressed and then changing scene. And about others things saving I honestly don’t know if they change, didn’t look into it. Inventory defenitly saves what items are in which slots. Changing scenes is tied to GameObject responsible for spawning persistend object

Yes, but the way the saving system works is through a coroutine.
The coroutine first saves the scene,
Then waits for SceneManager.LoadSceneAsych,
Then loads the scene.

Without being tied to a GameObject that is DontDestroyOnLoad(), the system can’t work. That’s what the portal does.

Saving system is tied to GamObject that is Don’tDestroyOnLoad()

Please paste in the script, or I won’t be able to figure out what’s wrong.

public class SavingWrapper : MonoBehaviour
{
    const string defaultSaveFile = "save";
    const string quickSaveFile = "quicksave";

    IEnumerator Start()
    {
        if (SceneManager.GetSceneByName("MainMenu") == SceneManager.GetActiveScene())
        {
            yield return null;
        }else
        {
            yield return GetComponent<SavingSystem>().LoadLastScene(defaultSaveFile);
        }
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.F6))
        {
            if (SceneManager.GetSceneByName("MainMenu") == SceneManager.GetActiveScene())
            {
                return;
            }
            else
            {
                Load(quickSaveFile);
            }
        }
        if(Input.GetKeyDown(KeyCode.F5)) 
        {
            if (SceneManager.GetSceneByName("MainMenu") == SceneManager.GetActiveScene())
            {
                return;
            }
            else
            {
                Save(quickSaveFile);
            }
        }

        if (Input.GetKey(KeyCode.T))
        {
            SceneManager.LoadSceneAsync(3);
        }
        if (Input.GetKey(KeyCode.R))
        {
            SceneManager.LoadScene(2);
        }
    }
    private void Load(string saveFile)
    {
        GetComponent<SavingSystem>().Load(saveFile);
    }

    private void Save(string saveFile)
    {
        GetComponent<SavingSystem>().Save(saveFile);
    }

    public void SaveFile()
    {
        GetComponent<SavingSystem>().Save(defaultSaveFile);
    }

    public void LoadFile()
    {
        GetComponent<SavingSystem>().Load(defaultSaveFile);
    }
    public class SavingSystem : MonoBehaviour
    {
        private int scene;
        public IEnumerator LoadLastScene(string saveFile)
        {

            Dictionary<string, object> state = LoadFile(saveFile);
            if (state.ContainsKey("lastSceneBuildIndex"))
            {
                //yield return SceneManager.LoadSceneAsync("LoadingScreen");
                int buildIndex = (int)state["lastSceneBuildIndex"];
                if(buildIndex != SceneManager.GetActiveScene().buildIndex)
                {
                    yield return SceneManager.LoadSceneAsync(buildIndex);
                }
            }
            RestoreState(state);
        }

        public void Save(string saveFile)
        {
            Dictionary<string, object> state = LoadFile(saveFile);
            CaptureState(state);
            SaveFile(saveFile, state);
        }

        public void Load(string saveFile)
        {
            StartCoroutine(LoadLastScene(saveFile));
            //RestoreState(LoadFile(saveFile));
        }
        public void Delete(string saveFile)
        {
            File.Delete(GetPathFromSaveFile(saveFile));
        }

        private Dictionary<string, object> LoadFile(string saveFile)
        {
            string path = GetPathFromSaveFile(saveFile);
            if (!File.Exists(path))
            {
                return new Dictionary<string, object>();
            }
            using (FileStream stream = File.Open(path, FileMode.Open))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                return (Dictionary<string, object>)formatter.Deserialize(stream);
            }
        }

        private void SaveFile(string saveFile, object state)
        {
            string path = GetPathFromSaveFile(saveFile);
            print("Saving to " + path);
            using (FileStream stream = File.Open(path, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, state);
            }
        }

        private void CaptureState(Dictionary<string, object> state)
        {
            foreach (SaveableEntity saveable in FindObjectsOfType<SaveableEntity>())
            {
                state[saveable.GetUniqueIdentifier()] = saveable.CaptureState();
            }
            state["lastSceneBuildIndex"] = SceneManager.GetActiveScene().buildIndex;
        }

        private void RestoreState(Dictionary<string, object> state)
        {
            foreach (SaveableEntity saveable in FindObjectsOfType<SaveableEntity>())
            {
                string id = saveable.GetUniqueIdentifier();
                if (state.ContainsKey(id))
                {
                    saveable.RestoreState(state[id]);
                }
            }
        }

        private string GetPathFromSaveFile(string saveFile)
        {
            return Path.Combine(Application.persistentDataPath, saveFile + ".sav");
        }
    }

and here is saving system

If I’m reading this correctly… pressing F5 saves to “quicksave”, and F6 loads the scene last saved in “quicksave”, pressing T loads scene 3 (but does not save or restore anything), and pressing R loads scene 2 (without saving or loading anything).

What I’m not seeing is something that saves the state, moves to the next scene, then loads the state all at once. Something like

IEnumerator SwitchScenes(int scene, string saveFile)
{
    GetComponent<SavingSystem>().Save(saveFile);
    yield return SceneManager.LoadSceneAsync(scene);
    GetComponent<SavingSystem>().Load(saveFile);
}

Even with this method, the way Load() is written in SavingWrapper would simply reload the scene that was saved in the first line, discarding the scene that was loaded in the yield return. The commented out line in Load() is the correct one for this context…

After adding this changes, equipment works as intented, but I don’t understand why inventory was transfering correctly.

That has me puzzled as well. :slight_smile:

And last question about Equipment. If I understand correctly as long as object has the same ID and Stats Equipment on them, then items on slots will be attached to them on every scene? And how does that works for prefabs created dynamically?

If you’re meaning items dropped in the scene with the RandomDropper, they should be saved automatically with the rest of the scene.

If you mean procedurally generated items, i.e. weapons with characteristics generated on the fly, that’s a whole different animal, and requires some modifications to the core of the Inventory system.

I mean something like equipment and team creation in xcom. On one scene you choose your team and give them equipment and in the second scene they are instantiated from script.

That’s a little trickier.
You’ll need to start with a way to procedurally spawn characters. I just completed a brief tutorial on saving procedurally spawned characters between scenes.

This could be a starting point, but I think it would require some tweaking before using for an XCom type team creation.

If your not planning saving the state of the levels themselves, just the player characters, then you could build your team in one scene, using the DynamicSaving to spawn them and manage them, then have a DynamicSaving in the next scene whose SaveableEntity has the same UniqueID (say “Team”).

I haven’t thought too much about adapting this to XCom style (or, for example, the many squad based games for mobile like Marvel: Strike Force, FFBE: War of the Visions, countless others). It’s possible that a better solution might be to adapt the Saving System to keep two saves… One, a master save file with all of the Characters and a global inventory, the other with a temporary save to assemble the squad. The state of all characters is kept in the master save. It’s in this menu scene that you would equip the players, set skills, etc. When assembling a team to go into battle, the selected characters would be registered with the DynamicSaving and saved in a temporary file. The battle scene would load the characters from the temporary save file and place them in their appropriate starting locations. Once the battle is over, the DynamicSaving saves the characters once again and when control returns to the main menu, the characters are read from the temporary save file and written back to the master save file.

I think I understand, thank you very much. It will probably take some time, but I know where to start

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms