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)
- This topic has 3 replies, 2 voices, and was last updated 8 months, 1 week ago by Terence.
-
AuthorPosts
-
February 5, 2024 at 5:20 am #13235AkaParticipant::
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; } } }
February 5, 2024 at 11:33 am #13238February 5, 2024 at 5:06 pm #13242AkaParticipant::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.
February 6, 2024 at 11:29 am #13247TerenceKeymaster::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.
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: