Forum begins after the advertisement:


[part 7] Map Update with Save/Load (improvement)

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [part 7] Map Update with Save/Load (improvement)

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #13235
    Aka
    Level 4
    Participant
    Helpful?
    Up
    1
    ::

    This is my code to update maps with the save/load system

    The reference names are those of my game, use yours.(ex: saveMachine = bench)

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.SceneManagement;
    
    [System.Serializable]
    public class SaveData
    {
        private static SaveData _instance;
        public static SaveData Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new SaveData();
                }
                return _instance;
            }
        }
    
        //map
        public HashSet<string> sceneNames = new HashSet<string>();
    public bool sMachineActivated = false;
    
        private SaveData() { }
    
        public void Initialize()
        {
    
            string mapPath = Application.persistentDataPath + "/save.maps.data";
    
            if (!File.Exists(mapPath))
            {
                File.Create(mapPath).Dispose();
            }
            if (sceneNames == null)
            {
                sceneNames = new HashSet<string>();
            }
            Debug.Log("SaveData Initialized.");
        }
    
        public void SaveMapData()
        {
            string path = Application.persistentDataPath + "/save.maps.data";
            using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)))
            {
                writer.Write(sMachineActivated);
                 writer.Write(sceneNames.Count);
                foreach (string sceneName in sceneNames)
                {
                    writer.Write(sceneName);
                }
                Debug.Log($"Maps saved: {string.Join(", ", sceneNames)}");
            }
        }
    
        public void LoadMapData()
        {
            string filePath = Application.persistentDataPath + "/save.maps.data";
    
            if (File.Exists(filePath) && new FileInfo(filePath).Length > 0)
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath)))
                {
                    sMachineActivated = reader.ReadBoolean();
                    int count = reader.ReadInt32();
                    sceneNames = new HashSet<string>();
                    for (int i = 0; i < count; i++)
                    {
                        sceneNames.Add(reader.ReadString());
                    }
                }
                Debug.Log($"Maps loaded: {string.Join(", ", sceneNames)}");
            }
            else
            {
                Debug.Log("No map save file found.");
            }
        }
    
        public void SaveMachineActivated()
        {
            sMachineActivated = true;
            SaveMapData(); 
        }
    }
    
    using System.Collections;
    using System.Collections.Generic;
    using TMPro.Examples;
    using UnityEngine;
    using Cinemachine;
    using UnityEngine.SceneManagement;
    using UnityEngine.EventSystems;
    
    public class GameManager : MonoBehaviour
    {
    
        [SerializeField] SaveMachine saveMachine;
    
        public static GameManager Instance { get; private set; }
    
        private void Awake()
        {
    
            SaveData.Instance.Initialize();
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
    
            }
    
            SaveData.Instance.LoadMapData();
    
            EnsureEventSystem();
            DontDestroyOnLoad(gameObject);
    
            saveMachine = FindObjectOfType<SaveMachine>();
        }
    
        private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            Debug.Log($"OnSceneLoaded: {scene.name}");
            SaveScene();
        }
        private void OnEnable()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
            Debug.Log("GameManager: Subscribed to sceneLoaded.");
        }
    
        private void OnDisable()
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            Debug.Log("GameManager: Unsubscribed from sceneLoaded.");
        }
        public void SaveScene()
        {
            string currentSceneName = SceneManager.GetActiveScene().name;
            SaveData.Instance.sceneNames.Add(currentSceneName);
            SaveData.Instance.SaveMapData();
            Debug.Log($"Scene saved: {currentSceneName}");
        }
    
        private void EnsureEventSystem()
        {
            if (FindObjectOfType<EventSystem>() == null)
            {
                GameObject eventSystemGO = new GameObject("EventSystem");
                eventSystemGO.AddComponent<EventSystem>();
                eventSystemGO.AddComponent<StandaloneInputModule>();
            }
        }
    
    }
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class UIManager : MonoBehaviour
    {
        public static UIManager Instance;
    
        public GameObject mapHandler;
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
                DontDestroyOnLoad(gameObject.transform.root.gameObject);
            }
    
        }
    
    }
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class MapManager : MonoBehaviour
    {
        public static MapManager Instance { get; private set; }
        [SerializeField] GameObject[] maps;
    
        SaveMachine saveMachine;
    
        private void Awake()
        {
            if (Instance == null)
            {
                Instance = this;
            }
            else if (Instance != this)
            {
                Destroy(gameObject);
            }
        }
    
        void Start()
        {
    
            UpdateMap();
        
        }
    
        public void UpdateMapState()
        {
            UpdateMap();
        }
    
    public void UpdateMap()
    {
        if (SaveData.Instance.sMachineActivated)
        {
            var savedScenes = SaveData.Instance.sceneNames;
            for (int i = 0; i < maps.Length; i++)
            {
                bool isActive = savedScenes.Contains("Map_" + (i + 1));
                maps[i].SetActive(isActive);
            }
            Debug.Log("Maps updated based on saved scenes.");
        }
    }
    
    }
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Events;
    using UnityEngine.SceneManagement;
    
    public class SaveMachine : MonoBehaviour
    {
    
        [HideInInspector] public PlayerController player;
    
        private bool isInMachine = false;
        private bool activeButton;
        [HideInInspector] public bool isSaving = false;
    
        private void Start()
        {
            player = PlayerController.Instance;
    
        }
    
        private void Update()
        {
            activeButton = player.dashButton;
            SaveActive();
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                isInMachine = true;
            }
        }
    
        private void OnTriggerExit2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                isInMachine = false;
                isSaving = false;
            }
        }
    
        private void SaveActive()
        {
            if (activeButton && isInMachine)
            {
    
                isSaving = true;
    
                SaveData.Instance.SaveMachineActivated();
                MapManager.Instance.UpdateMapState();
                string currentSceneName = SceneManager.GetActiveScene().name;
                SaveData.Instance.sceneNames.Add(currentSceneName);
                SaveData.Instance.SaveMapData();
                Debug.Log("Scenes saved: " + string.Join(", ", SaveData.Instance.sceneNames));
    
                //  StartCoroutine(Saving());
                isSaving = false;
    
            }
    
        }
    
    }
    has upvoted this post.
    #13238
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Thanks for sharing your code Aka.

    #13242
    Aka
    Level 4
    Participant
    Helpful?
    Up
    0
    ::

    You’re welcome.

    For prevent a null reference if you have savemachine (bench) in the very first scene with new game (no save file) when you active savemachine, modify in savemachine script the line MapManager.Instance.UpdateMapState(); This bug exist only in this circumstance.

            if (MapManager.Instance != null)
            {
                MapManager.Instance.UpdateMapState();
            }

    I have change the “struct” savedata to “class” savedata with singleton. I prefer, but maybe the script run with a “struct” like tutorial, I’ve no testing.

    #13247
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Thanks Aka. In most cases, a class should work the same as a struct. The only difference between them is that structs are passed by value, while classes are passed by reference. If you are not passing the value around between different variables, there shouldn’t be any difference.

    #16221
    pepecry
    Level 6
    Participant
    Helpful?
    Up
    0
    ::

    I know it kinda longtime ago but can I ask for what does this dashButton in
    activeButton = player.dashButton;
    mean? Does it the dash function in your game or something else? What would it do?

    #16255
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    I know it kinda longtime ago but can I ask for what does this dashButton in
    activeButton = player.dashButton;
    mean? Does it the dash function in your game or something else? What would it do?

    We will need to see the PlayerController script to be able to tell. He probably has a boolean there that detects whether the dash button is being pressed.

    #16257
    pepecry
    Level 6
    Participant
    Helpful?
    Up
    0
    ::

    He probably has a boolean there that detects whether the dash button is being pressed.

    So that for normal use, could we just assign the activeButton to true false base on the statement right?

Viewing 7 posts - 1 through 7 (of 7 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: