Forum begins after the advertisement:


[Part 7] How can I let the game have “new game”

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [Part 7] How can I let the game have “new game”

Viewing 15 posts - 1 through 15 (of 18 total)
  • Author
    Posts
  • #14912
    Elvin Sim
    Participant

    How can I let the game be like Start the game All over again and Replace the previous data(New Game button), and can also Load the game to let the player continue the game in previous data(Load Button(it like change the Start Button function to become Load button))

    #14913
    Elvin Sim
    Participant

    So that I can do multiple endings base on this function, just simple like player press the “New Game Button”, the previous data will be overwrite, player start the game all over again. Or player press the “Load Game Button”, back to the game and continue with the previous data

    #14914
    Joseph Tang
    Moderator

    If you want to have two buttons, a “New Game” & “Load Game” button, just use the same code that i provided in the previous post for deleting save files.

    [Part 7] My Maps cannot update when I quit the game

    Then, for your scene buttons, change the on-click event properties on the button components to:

    “New Game”: Delete all saved data > Load starting scene.

    “Load Game”: Load starting scene.

    #14918
    Elvin Sim
    Participant
    [System.Serializable]
    public struct SaveData
    {
        public static SaveData Instance;
    
        //map stuff
        public HashSet<string> sceneNames;
    
        //bench stuff
        public string benchSceneName;
        public Vector2 benchPos;
    
        //player stuff
        public int playerHealth;
        public int playerMaxHealth;
        public int playerHeartShards;
        public float playerMana;
        public int playerManaOrbs;
        public int playerOrbShard;
        public float playerOrb0fill, playerOrb1fill, playerOrb2fill;
        public bool playerHalfMana;
        public Vector2 playerPosition;
        public string lastScene;
    
        public bool playerUnlockedWallJump, playerUnlockedDash, playerUnlockedVarJump;
        public bool playerUnlockedSideCast, playerUnlockedUpCast, playerUnlockedDownCast;
       
        //enemies stuff
        //shade
        public Vector2 shadePos;
        public string sceneWithShade;
        public Quaternion shadeRot;
    
        public void Initialize()
        {
            if(!File.Exists(Application.persistentDataPath + "/save.scenes.data"))
            {
                using (BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.scenes.data")))
                {
                    writer.Write(0); // Write an empty scene data structure
                }
            }
    
            // Load existing data
            LoadSceneData();
    
            if (!File.Exists(Application.persistentDataPath + "/save.bench.data")) 
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.bench.data"));
            }
    
            if (!File.Exists(Application.persistentDataPath + "/save.player.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.player.data"));
            }
    
            if (!File.Exists(Application.persistentDataPath + "/save.shade.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.shade.data"));
            }
    
            if (sceneNames == null)
            {
                sceneNames = new HashSet<string>();
            }
        }
    
        #region Scenes Stuff
        public void SaveSceneData()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.scenes.data")))
            {
                writer.Write(sceneNames.Count);
                foreach (string sceneName in sceneNames)
                {
                    writer.Write(sceneName);
                }
            }
        }
    
        public void LoadSceneData()
        {
            if (File.Exists(Application.persistentDataPath + "/save.scenes.data"))
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.scenes.data")))
                {
                    int sceneCount = reader.ReadInt32();
                    sceneNames = new HashSet<string>();
                    for (int i = 0; i < sceneCount; i++)
                    {
                        string sceneName = reader.ReadString();
                        sceneNames.Add(sceneName);
                    }
                }
            }
            else
            {
                sceneNames = new HashSet<string>();
            }
        }
        #endregion
    
        /*#region Delete save data
        public void DeleteScenesData()
        {
            string path = Application.persistentDataPath + "/save.scenes.data";
            if (File.Exists(path))
            {
                File.Delete(path);
                Debug.Log("Scenes data deleted.");
            }
        }
    
        public void DeleteBenchData()
        {
            string path = Application.persistentDataPath + "/save.bench.data";
            if (File.Exists(path))
            {
                File.Delete(path);
                Debug.Log("Bench data deleted.");
            }
        }
    
        public void DeletePlayerData()
        {
            string path = Application.persistentDataPath + "/save.player.data";
            if (File.Exists(path))
            {
                File.Delete(path);
                Debug.Log("Player save data deleted.");
            }
        }
    
        public void DeleteShadeData()
        {
            string path = Application.persistentDataPath + "/save.shade.data";
            if (File.Exists(path))
            {
                File.Delete(path);
                Debug.Log("Shade save data deleted.");
            }
        }
    
        public void DeleteAllSaveData()
        {
            DeleteScenesData();
            DeleteBenchData();
            DeletePlayerData();
            DeleteShadeData();
        }
    
        #endregion */
    
        #region Bench Stuff
        public void SaveBench()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.bench.data")))
            {
                writer.Write(benchSceneName);
                writer.Write(benchPos.x);
                writer.Write(benchPos.y);
            }
        }
    
        public void LoadBench()
        {
            string savePath = Application.persistentDataPath + "/save.bench.data";
            if (File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.bench.data")))
                {
                    benchSceneName = reader.ReadString();
                    benchPos.x = reader.ReadSingle();
                    benchPos.y = reader.ReadSingle();
                }
            }
            else
            {
                Debug.Log("Bench doesn't exist");
            }
        }
        #endregion
    
        #region Player Stuff
        public void SavePlayerData()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data")))
            {
                playerHealth = PlayerController.Instance.Health;
                writer.Write(playerHealth);
                playerMaxHealth = PlayerController.Instance.maxHealth;
                writer.Write(playerMaxHealth);
                playerHeartShards = PlayerController.Instance.heartShards;
                writer.Write(playerHeartShards);
    
                playerMana = PlayerController.Instance.Mana;
                writer.Write(playerMana);
                playerHalfMana = PlayerController.Instance.halfMana;
                writer.Write(playerHalfMana);
                playerManaOrbs = PlayerController.Instance.manaOrbs;
                writer.Write(playerManaOrbs);
                playerOrbShard = PlayerController.Instance.orbShard;
                writer.Write(playerOrbShard);
                playerOrb0fill = PlayerController.Instance.manaOrbsHandler.orbFills[0].fillAmount;
                writer.Write(playerOrb0fill);
                playerOrb1fill = PlayerController.Instance.manaOrbsHandler.orbFills[1].fillAmount;
                writer.Write(playerOrb1fill);
                playerOrb2fill = PlayerController.Instance.manaOrbsHandler.orbFills[2].fillAmount;
                writer.Write(playerOrb2fill);
    
                playerUnlockedWallJump = PlayerController.Instance.unlockedWallJump;
                writer.Write(playerUnlockedWallJump);
                playerUnlockedDash = PlayerController.Instance.unlockedDash;
                writer.Write(playerUnlockedDash);
                playerUnlockedVarJump = PlayerController.Instance.unlockedVarJump;
                writer.Write(playerUnlockedVarJump);
    
                playerUnlockedSideCast = PlayerController.Instance.unlockedSideCast;
                writer.Write(playerUnlockedSideCast);
                playerUnlockedUpCast = PlayerController.Instance.unlockedUpCast;
                writer.Write(playerUnlockedUpCast);
                playerUnlockedDownCast = PlayerController.Instance.unlockedDownCast;
                writer.Write(playerUnlockedDownCast);
                
    
                playerPosition = PlayerController.Instance.transform.position;
                writer.Write(playerPosition.x);
                writer.Write(playerPosition.y);
    
                lastScene = SceneManager.GetActiveScene().name;
                writer.Write(lastScene);
    
            }
            Debug.Log("saved player data");
        }
    
        public void LoadPlayerData()
        {
            string savePath = Application.persistentDataPath + "/save.player.data";
            if (File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.player.data")))
                {
                    playerHealth = reader.ReadInt32();
                    playerMaxHealth = reader.ReadInt32();
                    playerHeartShards = reader.ReadInt32();
                    playerMana = reader.ReadSingle();
                    playerHalfMana = reader.ReadBoolean();
                    playerManaOrbs = reader.ReadInt32();
                    playerOrbShard = reader.ReadInt32();
                    playerOrb0fill = reader.ReadSingle();
                    playerOrb1fill = reader.ReadSingle();
                    playerOrb2fill = reader.ReadSingle();
    
                    playerUnlockedWallJump = reader.ReadBoolean();
                    playerUnlockedDash= reader.ReadBoolean();
                    playerUnlockedVarJump= reader.ReadBoolean();
    
                    playerUnlockedSideCast= reader.ReadBoolean();
                    playerUnlockedUpCast = reader.ReadBoolean();
                    playerUnlockedDownCast = reader.ReadBoolean();
    
                    playerPosition.x = reader.ReadSingle();
                    playerPosition.y = reader.ReadSingle();
    
                    lastScene = reader.ReadString();
    
                    SceneManager.LoadScene(lastScene);
                    PlayerController.Instance.transform.position = playerPosition;
                    PlayerController.Instance.halfMana = playerHalfMana;
                    PlayerController.Instance.Health = playerHealth;
                    PlayerController.Instance.maxHealth = playerMaxHealth;
                    PlayerController.Instance.heartShards = playerHeartShards;
                    PlayerController.Instance.Mana = playerMana;
                    PlayerController.Instance.manaOrbs = playerManaOrbs;
                    PlayerController.Instance.orbShard = playerOrbShard;
                    PlayerController.Instance.manaOrbsHandler.orbFills[0].fillAmount = playerOrb0fill;
                    PlayerController.Instance.manaOrbsHandler.orbFills[1].fillAmount = playerOrb1fill;
                    PlayerController.Instance.manaOrbsHandler.orbFills[2].fillAmount = playerOrb2fill;
    
                    PlayerController.Instance.unlockedWallJump = playerUnlockedWallJump;
                    PlayerController.Instance.unlockedDash = playerUnlockedDash;
                    PlayerController.Instance.unlockedVarJump = playerUnlockedVarJump;
    
                    PlayerController.Instance.unlockedSideCast = playerUnlockedSideCast;
                    PlayerController.Instance.unlockedUpCast = playerUnlockedUpCast;
                    PlayerController.Instance.unlockedDownCast = playerUnlockedDownCast;
    
                }
                Debug.Log("load player data");
                Debug.Log(playerHalfMana);
            }
            else
            {
                Debug.Log("File doesn't exist");
                PlayerController.Instance.halfMana = false;
                PlayerController.Instance.Health = PlayerController.Instance.maxHealth;
                PlayerController.Instance.Mana = 0.5f;
                PlayerController.Instance.heartShards = 0;
    
                PlayerController.Instance.unlockedWallJump = false;
                PlayerController.Instance.unlockedDash = false;
                PlayerController.Instance.unlockedVarJump = false;
    
                PlayerController.Instance.unlockedSideCast = false;
                PlayerController.Instance.unlockedUpCast = false;
                PlayerController.Instance.unlockedDownCast = false;
    
            }
        }
        #endregion
    
        #region Shade Stuff
        public void SaveShadeData()
        {
            using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.shade.data")))
            {
                sceneWithShade = SceneManager.GetActiveScene().name;
                shadePos = Shade.Instance.transform.position;
                shadeRot = Shade.Instance.transform.rotation;
    
                writer.Write(sceneWithShade);
    
                writer.Write(shadePos.x);
                writer.Write(shadePos.y);
    
                writer.Write(shadeRot.x);
                writer.Write(shadeRot.y);
                writer.Write(shadeRot.z);
                writer.Write(shadeRot.w);
            }
        }
    
        public void LoadShadeData()
        {
            string savePath = Application.persistentDataPath + "/save.shade.data";
            if (File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.shade.data")))
                {
                    sceneWithShade = reader.ReadString();
                    shadePos.x = reader.ReadSingle();
                    shadePos.y = reader.ReadSingle();
    
                    float rotationX = reader.ReadSingle();
                    float rotationY = reader.ReadSingle();
                    float rotationZ = reader.ReadSingle();
                    float rotationW = reader.ReadSingle();
                    shadeRot = new Quaternion(rotationX, rotationY, rotationZ, rotationW);
                }
                Debug.Log("Load shade data");
            }
            else
            {
                Debug.Log("Shade doesn't exist");
            }
        }
        #endregion
    }
    #14919
    Elvin Sim
    Participant
    public class GameManager : MonoBehaviour
    {
        public string transitionedFromScene;
    
        public Vector2 platformingRespawnPoint;
        public Vector2 respawnPoint;
        [SerializeField] Bench bench;
    
        public GameObject shade;
    
        [SerializeField] private FadeUI pauseMenu;
        [SerializeField] private float fadeTime;
        public bool gameIsPaused;
    
        public static GameManager Instance {  get; private set; }
        private void Awake()
        {
            SaveData.Instance.Initialize();
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
    
            if(PlayerController.Instance != null)
            {
                if (PlayerController.Instance.halfMana)
                {
                    SaveData.Instance.LoadShadeData();
                    if(SaveData.Instance.sceneWithShade == SceneManager.GetActiveScene().name || SaveData.Instance.sceneWithShade == "")
                    {
                        Instantiate(shade, SaveData.Instance.shadePos, SaveData.Instance.shadeRot);
                    }
                }
            }
    
            SaveScene();
            ResetData();
    
            DontDestroyOnLoad(gameObject);
            bench = FindObjectOfType<Bench>();
        }
    
        public void ResetData()
        {
            //SaveData.Instance.DeleteAllSaveData();
        }
    
        private void Update()
        {
            if(Input.GetKeyDown(KeyCode.Escape) && !gameIsPaused)
            {
                pauseMenu.FadeUIIn(fadeTime);
                Time.timeScale = 0;
                gameIsPaused = true;
            }
        }
    
        public void UnpauseGame()
        {
            Time.timeScale = 1;
            gameIsPaused = false;
        }
    
        public void SaveScene()
        {
            string currentSceneName = SceneManager.GetActiveScene().name;
            SaveData.Instance.sceneNames.Add(currentSceneName);
            SaveData.Instance.SaveSceneData(); // Save scene names whenever a new scene is added
        }
    
        public void RespawnPlayer()
        {
            SaveData.Instance.LoadBench();
            if (SaveData.Instance.benchSceneName != null) //load the bench's scene if it exists.
            {
                SceneManager.LoadScene(SaveData.Instance.benchSceneName);
            }
    
            if(SaveData.Instance.benchPos != null) //set the respawn point to the bench's position.
            {
                respawnPoint = SaveData.Instance.benchPos;
            }
            else
            {
                respawnPoint = platformingRespawnPoint;
            }
    
            PlayerController.Instance.transform.position = respawnPoint;
    
            StartCoroutine(UIManager.Instance.DeactivateDeathScreen());
            PlayerController.Instance.Respawned();
        }
      
    }
    #14920
    #14921
    Elvin Sim
    Participant

    As you can see I comment the entire delete code because it will crash with the save code, For the On Click property for button, I don’t know how to do, so I do it like the video as you can see, but cannot work also, can you teach me how to do this thks

    #14922
    #14923
    Joseph Tang
    Moderator

    1. The problem with running the delete code is that your GameManager.cs is calling ResetData() on Awake(). Simply, remove that line and you can uncomment your code again.

        private void Awake()
        {
            SaveData.Instance.Initialize();
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
    
            if(PlayerController.Instance != null)
            {
                if (PlayerController.Instance.halfMana)
                {
                    SaveData.Instance.LoadShadeData();
                    if(SaveData.Instance.sceneWithShade == SceneManager.GetActiveScene().name || SaveData.Instance.sceneWithShade == "")
                    {
                        Instantiate(shade, SaveData.Instance.shadePos, SaveData.Instance.shadeRot);
                    }
                }
            }
    
            SaveScene();
            ResetData();
    
            DontDestroyOnLoad(gameObject);
            bench = FindObjectOfType();
        }
    
        public void ResetData()
        {
            //SaveData.Instance.DeleteAllSaveData();
        }

    2. Because there is no GameManager.cs in the main menu and it has destroy in main menu, it cannot be used to call the deletion of the data. Furthermore, there is no SaveData instance initialized in the main menu since GameManager.cs needs to be present to initialize it on Awake(). Thus, you will not have access to both SaveData.cs and GameManager.cs. Thus, you will have to create a new script.

    public void ResetSaveData()
    {
        string path = Application.persistentDataPath;
    
        // Delete all save files if they exist
        string[] saveFiles = {
            path + "/save.scenes.data",
            path + "/save.bench.data",
            path + "/save.player.data",
            path + "/save.shade.data"
        };
    
        foreach (string saveFile in saveFiles)
        {
            try
            {
                if (File.Exists(saveFile))
                {
                    File.Delete(saveFile);
                    Debug.Log(saveFile + " deleted.");
                }
            }
            catch (System.Exception ex)
            {
                Debug.LogError("Failed to delete " + saveFile + ": " + ex.Message);
            }
        }
    
        Debug.Log("All save data has been reset.");
    }

    This is not tested, but using this script, place it on an empty game object and give that object an appropriate name like “SaveDataManager”, in the MainMenu scene. Then, add it to your “New Game” button’s on-click events. Remember to put this function above the loading scene function since you want it to reset the data before the scene is loaded.

    Also, note that your “Load Game” button does not need nor should want a save game on-click event. Just leave it as is since you only want it to play the game.

    #14929
    Elvin Sim
    Participant

    Thank you very much it works perfectly!!!

    #14967
    MI NI
    Participant

    When I Reset the game
    The new game will start at the last saved SavePoint (Bench in the tutorial, an owl in the video)
    But other Data has Reset.
    In addition, if you press Esc to pause during FadeIn&Out, the game will stop.

    The first button in the video is to create a new game
    The second button is to continue the game

    This cs. was made by referring to the article of #14923

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    
    public class MenuSaveData : MonoBehaviour
    {
        public void ResetSaveData()
        {
            string path = Application.persistentDataPath;
    
            // Delete all save files if they exist
            string[] saveFiles = {
            path + "/save.savePoint.data",
            path + "/save.player.data",
            path + "/save.scenes.data",
            //path + "/save.shade.data"
        };
    
            foreach (string saveFile in saveFiles)
            {
                try
                {
                    if (File.Exists(saveFile))
                    {
                        File.Delete(saveFile);
                        Debug.Log(saveFile + " deleted.");
                    }
                }
                catch (System.Exception ex)
                {
                    Debug.LogError("Failed to delete " + saveFile + ": " + ex.Message);
                }
            }
    
            Debug.Log("All save data has been reset.");
        }
    
        // Start is called before the first frame update
        void Start()
        {
            
        }
    
        // Update is called once per frame
        void Update()
        {
            
        }
    }
    

    SaveData.cs

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.SceneManagement;
    
    [System.Serializable]
    
    public struct SaveData
    {
        public static SaveData Instance;
    
        //map stuff
        public HashSet<string> sceneName;
    
        //savePoint stuff
        public string savePointSceneName;
        public Vector2 savePointPos;
    
        //player stuff
        public float playerHealth;
        public float playerMana;
        public Vector2 playerPosition;
        public string lastScene;
    
        //player skill
        public bool playerUnlockWallJump;
        public bool playerUnlockDash;
        public bool playeUnlockAirJump;
        public bool playerUnlockSideSpell;
    
        public void Initialize() //初始化
        {
    
            if (!File.Exists(Application.persistentDataPath + "/save.scenes.data"))
            {
                using (BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.scenes.data")))
                {
                    writer.Write(0); // Write an empty scene data structure
                }
            }
            LoadSceneData();
    
            //檢查重生點文件是否存在,否則將建立一個新文件
            if (!File.Exists(Application.persistentDataPath + "/save.savePoint.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.savePoint.data"));
            }
            //檢查玩家文件是否存在,否則將建立一個新文件
            if (!File.Exists(Application.persistentDataPath + "/save.player.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.player.data"));
            }
    
            if (sceneName == null) //檢查此場景是否為空 
            {
                //如果是,建立一個新HashSet
                sceneName = new HashSet<string>();
            }
        }
    
        public void SavedSavePoint()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.savePoint.data")))
            {
                Debug.Log("SavedSavePoint");
                writer.Write(savePointSceneName);
                writer.Write(savePointPos.x);
                writer.Write(savePointPos.y);
            }
        }
    
        public void LoadSavePoint()
        {
            //if(File.Exists(Application.persistentDataPath + "/save.savePoint.data"))
            string savePath = Application.persistentDataPath + "/save.savePoint.data";
            if(File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.savePoint.data")))
                {
                    Debug.Log("LoadSavePoint");
                    savePointSceneName = reader.ReadString();
                    savePointPos.x = reader.ReadSingle();
                    savePointPos.y = reader.ReadSingle();
                }
            }
            else
            {
                Debug.Log("SavePoint do not exits");
            }
        }
    
        public void SavePlayerData() //備註:存儲的順序需與讀取的順序相同,否則可能出錯
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data")))
            {
                //血量、魔力
                playerHealth = PlayerController.Instance.Health;
                writer.Write(playerHealth);
                playerMana = PlayerController.Instance.Mana;
                writer.Write(playerMana);
                //技能
                playerUnlockWallJump = PlayerController.Instance.unlockWallJump;
                writer.Write(playerUnlockWallJump);
                playerUnlockDash = PlayerController.Instance.unlockDash;
                writer.Write(playerUnlockDash);
                playeUnlockAirJump = PlayerController.Instance.unlockAirJump;
                writer.Write(playeUnlockAirJump);
    
                playerUnlockSideSpell = PlayerController.Instance.unlockSideSpell;
                writer.Write(playerUnlockSideSpell);
    
                //位置
                playerPosition = PlayerController.Instance.transform.position;
                writer.Write(playerPosition.x);
                writer.Write(playerPosition.y);
                //場景
                lastScene = SceneManager.GetActiveScene().name;
                writer.Write(lastScene);
    
                
    
            }
        }
    
        public void LoadPlayerData()
        {
            //string savePath = Application.persistentDataPath + "/save.player.data";
            //if (File.Exists(savePath) && new FileInfo(savePath).Length > 0) 
            if(File.Exists(Application.persistentDataPath + "/save.player.data"))
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.player.data")))
                {
                    //血量、魔力
                    playerHealth = reader.ReadSingle();
                    playerMana = reader.ReadSingle();
                    //技能
                    playerUnlockWallJump = reader.ReadBoolean();
                    playerUnlockDash = reader.ReadBoolean();
                    playeUnlockAirJump = reader.ReadBoolean();
    
                    playerUnlockSideSpell = reader.ReadBoolean();
    
                    //位置
                    playerPosition.x = reader.ReadSingle();
                    playerPosition.y = reader.ReadSingle();
                    //場景
                    lastScene = reader.ReadString();
    
                    SceneManager.LoadScene(lastScene);
                    PlayerController.Instance.transform.position = playerPosition;
                    PlayerController.Instance.Health = playerHealth;
                    PlayerController.Instance.Mana = playerMana;
    
                    PlayerController.Instance.unlockWallJump = playerUnlockWallJump;
                    PlayerController.Instance.unlockDash = playerUnlockDash;
                    PlayerController.Instance.unlockAirJump = playeUnlockAirJump;
    
                    PlayerController.Instance.unlockSideSpell = playerUnlockSideSpell;
                }
                Debug.Log("load player data");
            }
            else
            {
                //如果檔案不存在
                Debug.Log("File doesnt exist");
                PlayerController.Instance.Health = PlayerController.Instance.maxHealth;
                PlayerController.Instance.Mana = 0.5f;
                PlayerController.Instance.unlockWallJump = false;
                PlayerController.Instance.unlockDash = false;
                PlayerController.Instance.unlockAirJump = false;
                PlayerController.Instance.unlockSideSpell = false;
            }
        }
    
        public void SaveSceneData()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.scenes.data")))
            {
                writer.Write(sceneName.Count);
                foreach (string sceneName in sceneName)
                {
                    writer.Write(sceneName);
                }
            }
        }
    
        public void LoadSceneData()
        {
            if (File.Exists(Application.persistentDataPath + "/save.scenes.data"))
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.scenes.data")))
                {
                    int sceneCount = reader.ReadInt32();
                    sceneName = new HashSet<string>();
                    for (int i = 0; i < sceneCount; i++)
                    {
                        string _sceneName = reader.ReadString();
                        sceneName.Add(_sceneName);
                    }
                }
            }
            else
            {
                sceneName = new HashSet<string>();
            }
        }
    
        public void DeleteScenesData()//刪除資料
        {
            string path = Application.persistentDataPath + "/save.scenes.data";
            if (File.Exists(path))
            {
                File.Delete(path);
                Debug.Log("Scenes data deleted.");
            }
        }
    
        public void DeletePlayerData()//刪除資料
        {
            string path = Application.persistentDataPath + "/save.player.data";
            if (File.Exists(path))
            {
                File.Delete(path);
                Debug.Log("Player save data deleted.");
            }
        }
    
        public void DeleteSavePointData()//刪除資料
        {
            string path = Application.persistentDataPath + "/save.player.data";
            if (File.Exists(path))
            {
                File.Delete(path);
                Debug.Log("Player save data deleted.");
            }
        }
    
        public void DeleteAllSaveData()
        {
            DeleteScenesData();
            DeleteSavePointData();
            DeletePlayerData();
        }
    
    }
    

    GameManager.cs

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class GameManager : MonoBehaviour
    {
        public string transitionedFromScene;  //儲存前一個場景
    
        public Vector2 platformingRespawnPoint; //重生點
        public Vector2 respawnPoint;
        [SerializeField] SavePoint savePoint;
    
        [SerializeField] private FadeUI pauseMenu;
        [SerializeField] private float fadeTime;
        public bool gameIsPaused;
        //public bool Resumed = false;
    
        public static GameManager Instance { get; private set; }
        private void Awake()
        {
            SaveData.Instance.Initialize();
    
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
    
            SaveScene();
    
            DontDestroyOnLoad(gameObject);
    
            savePoint = FindObjectOfType<SavePoint>();
        }
    
        private void Update()
        {
            if(Input.GetKeyDown(KeyCode.P)) //test
            {
                Debug.Log("P");
                SaveData.Instance.SavePlayerData();
            }
    
            if(Input.GetKeyDown(KeyCode.Escape) && !gameIsPaused)
            {
                pauseMenu.FadeUIIn(fadeTime);
                Time.timeScale = 0;
                GameManager.Instance.gameIsPaused = true;
            }
    
            if(Input.GetKeyDown(KeyCode.R)) //test
            {
                SaveData.Instance.DeleteAllSaveData();
            }
        }
        public void SaveGame()
        {
            SaveData.Instance.SavePlayerData();
        }
    
        public void UnpauseGame()
        {
            //Resumed = true;
            Time.timeScale = 1;
            GameManager.Instance.gameIsPaused = false; //沒有正確銷毀gamemanager 導致需要加GameManager.Instance
            Debug.Log(gameIsPaused);
        }
    
        public void SaveScene()
        {
            //獲取當前場景名稱
            string currentSceneName = SceneManager.GetActiveScene().name;
            SaveData.Instance.sceneName.Add(currentSceneName);
            Debug.Log("saved" + currentSceneName);
        }
    
        public void RespawnPlayer()
        {
            SaveData.Instance.LoadSavePoint();
    
            if(SaveData.Instance.savePointSceneName != null) //載入重生點的場景
            {
                SceneManager.LoadScene(SaveData.Instance.savePointSceneName);
            }
    
            if(SaveData.Instance.savePointPos != null) //設定重生位置為savePoint
            {
                respawnPoint = SaveData.Instance.savePointPos;
            }
            else
            {
                respawnPoint = platformingRespawnPoint;
            }
    
            PlayerController.Instance.transform.position = respawnPoint;
    
            StartCoroutine(UIManager.Instance.DeactiveateDeathScreen()); //重生點淡入淡出
            PlayerController.Instance.Respawned(); //使角色重生
        }
    }
    
    #14968
    Joseph Tang
    Moderator

    I’ll need you to test out some things. Looking at your video, it appears that the new game did work once. But a subsequent use failed.

    Try putting a print/debug log into your LoadPlayerData() after lastScene = reader.ReadString(); to check whether your lastScene is intact. Since your LoadPlayerData() is the only means of changing scenes on start, it would mean that lastScene is not being cleared correctly, and not quite the bench.

    Could you also try replicating the issue while showing the console for the Debug.Log(saveFile + " deleted."); instead of the save point?

    But lets also test out another thing here after you test out the above bug test.
    Initialize the SaveData.cs in your MenuSaveData.cs, this will allow you to make changes to it will in main menu. Then, forcefully change the lastScene to your tutorial scene.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    
    public class MenuSaveData : MonoBehaviour
    {
        public void ResetSaveData()
        {
            string path = Application.persistentDataPath;
    
            // Delete all save files if they exist
            string[] saveFiles = {
                path + "/save.savePoint.data",
                path + "/save.player.data",
                path + "/save.scenes.data"
            };
    
            foreach (string saveFile in saveFiles)
            {
                try
                {
                    if (File.Exists(saveFile))
                    {
                        File.Delete(saveFile);
                        Debug.Log(saveFile + " deleted.");
                    }
                }
                catch (System.Exception ex)
                {
                    Debug.LogError("Failed to delete " + saveFile + ": " + ex.Message);
                }
            }
    
            // Ensure SaveData is initialized
            SaveData.Instance.Initialize();
    
            // Forcefully set the tutorial scene
            SaveData.Instance.lastScene = new string("your first scene")
    
            Debug.Log("All save data has been reset.");
        }
    }

    This additional line of code should help to always load the correct starting scene with the main menu button.
    Also, ensure that this code is called before loading the scene in your New game button’s onclick event.

    #15015
    MI NI
    Participant

    I tested them and they didn’t work.

    
    // Ensure SaveData is initialized
            SaveData.Instance.Initialize();
    
    // Forcefully set the tutorial scene
            SaveData.Instance.lastScene = new string("your first scene")
    

    I made some attempts in the video, but I’m not sure, where Debug.Log(saveFile + “deleted.”); should be placed.
    If you need me to try more, please be sure to let me know

    #15016
    Joseph Tang
    Moderator

    Okay, taking a look at your video, can you show me your PlayerController.cs code?

    1. Your Console shows the debug log, “SavePoint do not exits”, from SaveData.cs, being called by your PlayerController.cs Start() and your GameManager.cs RespawnPlayer().

    > This means that your player controller is telling the game manager to respawn them. The respawn method takes a look into the savepoint data from the SaveData.cs and finds nothing, thus returning the debug log.

    > But why is your Player calling to respawn? It should be loading with max health based on your LoadPlayerData()‘s else function. Or even through the code in the PlayerController.cs Start(), thus I need to check this.


    2. Add a check to see if the RespawnPlayer() is what’s causing the issue. The player doesn’t appear to have any other data that isnt deleted, but rather only seems to start the game at the last save point saved at.

    > Thus, Try testing the issue from “Scene 1” instead of “Scene 2”. You’ve been going to Scene 2 to test this, but we need to see if this issue is only prominent in sending the player to “Scene 2″‘s save point, or can send the player to “Scene 1″‘s save point as well after deleting the save file.

    > Also, Add a check to see whether the RespawnPlayer() has found a save point despite seeing that there is no save point data by adding this print/debug log.

    GameManager.cs

        public void RespawnPlayer()
        {
            SaveData.Instance.LoadSavePoint();
    
            if(SaveData.Instance.savePointSceneName != null) //載入重生點的場景
            {
                print(SaveData.Instance.savePointSceneName);
                SceneManager.LoadScene(SaveData.Instance.savePointSceneName);
            }
    
            ...
        }
    #15020
    MI NI
    Participant

    In the video, I tested scene 1 and scene 3

    PlayerController.cs

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UIElements;
    using UnityEngine.UI;
    
    public class PlayerController : MonoBehaviour
    {
        [Header("水平移動")]
        [SerializeField] private float walkSpeed = 1; //走路速度
        [Space(5)]
    
        [Header("垂直移動")]
        [SerializeField] private float jumpForce = 45; //跳躍速度
        private int jumpBufferCounter = 0; //按下按鈕儲存指令
        [SerializeField] private int jumpBufferFrames; //按下按鈕儲存指令時間/偵
        private float coyoteTimeCounter = 0; //土狼時間
        [SerializeField] private float coyoteTime; //土狼時間/偵
        private int airJumpCounter = 0;
        [SerializeField] private int maxAirJumps;
        [Space(5)]
    
        [Header("WallJump")]
        [SerializeField] private float wallSlidingSpeed; //滑行速度
        [SerializeField] private Transform wallCheck; //牆壁檢查點
        [SerializeField] private LayerMask wallLayer;
        [SerializeField] private float wallJumpingDuration; //蹬牆跳持續時間
        [SerializeField] private Vector2 wallJumpPower;
        float wallJumpingDirection;
        bool isWallSliding;
        bool isWallJumping;
        [Space(5)]
    
        [Header("地面檢查")]
        [SerializeField] private Transform groundCheckPoint; //地面檢查點
        [SerializeField] private float groundCheckY = 0.2f; //
        [SerializeField] private float groundCheckX = 0.5f; //
        [SerializeField] private LayerMask whatIsGround; //檢查是否在地面
        [Space(5)]
    
        [Header("衝刺")]
        [SerializeField] private float dashSpeed; //衝刺速度
        [SerializeField] private float dashTime;  //衝刺時間
        [SerializeField] private float dashCooldown; //衝刺冷卻
        [Space(5)]
    
        [Header("攻擊")]
    
        [SerializeField] private float timeBetweenAttack; //攻擊時間
        [SerializeField] Transform SideAttackTransform, UpAttackTransform, DownAttackTransform; //不同方向攻擊位置
        [SerializeField] Vector2 SideAttackArea, UpAttackArea, DownAttackArea; //不同方向攻擊範圍
        [SerializeField] LayerMask attackableLayer; //可攻擊的對象圖層
        [SerializeField] float damage; //傷害
        [SerializeField] GameObject slashEffect; //劍氣效果
        private bool attack = false;
        private float timeSinceAttack;
    
        bool restoreTime;
        public float restoreTimeSpeed;
    
        [Space(5)]
    
        [Header("後座力")]
        [SerializeField] int recilXSteps = 5;
        [SerializeField] int recilYSteps = 5;
    
        [SerializeField] float recilXSpeed = 5;
        [SerializeField] float recilYSpeed = 5;
    
        private int stepsXRecoiled, stepsYRecoiled;
        [Space(5)]
    
        [Header("血量")]
        public float health;  //當前血量
        public float maxHealth; //最大血量
        [SerializeField] GameObject hitEffect; //被擊效果
        [SerializeField] float hitFlashSpeed; //受傷角色閃爍
        float healTimer;
        [SerializeField] float timeToheal;
        [Space(5)]
    
        [Header("血量圖片")]
        public Text healthText; //血量文字
        public UnityEngine.UI.Image healthImage; //血量圖片
        public UnityEngine.UI.Image healthEffect; //血量緩衝特效
        public float effectTime = 0.5f; //緩衝持續時間
        private Coroutine updateCoroutine;
        [Space(5)]
    
        [Header("魔力")]
        [SerializeField] float mana; //魔力量
        [SerializeField] float maxMana; //最大魔力量
        [SerializeField] float manaDrainSpeed; //魔力消耗速度
        [SerializeField] float manaGain; //獲得魔力
        [SerializeField] UnityEngine.UI.Image manaImage; //魔力圖片
    
        [Header("技能")]
        [SerializeField] float manaSpellCost = 0.3f; //法術消耗
        [SerializeField] float timeBetweenCast = 0.5f; //攻擊間隔速度
        [SerializeField] GameObject sideSpellFireball; //火球物件Prefab
        float timeSinceCast;
        float castOrHealtimer;
    
        [Header("相機")]
        [SerializeField] private float playerFallSpeedTheshold = -10;
    
        [HideInInspector] public PlayerStateList pState; //取得緩衝輸入腳本
        [HideInInspector] public Rigidbody2D rb;
        private Animator anim;
        private SpriteRenderer sr;
    
        private float gravity; //重力
        private float xAxis, yAxis; //取得XY軸輸入
        private bool canDash = true;
        private bool dashed;
        private bool canFlash = true;
        bool openMap; //開啟地圖
        bool openInventory; //開啟背包
    
        //解鎖能力
        public bool unlockWallJump;
        public bool unlockDash;
        public bool unlockAirJump;
        public bool unlockSideSpell = false;
        
    
        public static PlayerController Instance; //防止多個重複的玩家腳本出現
        private void Awake()
        {
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
    
            DontDestroyOnLoad(gameObject);
    
            
        }
    
        void Start()
        {
            pState = GetComponent<PlayerStateList>();
    
            rb = GetComponent<Rigidbody2D>();
    
            sr = GetComponent<SpriteRenderer>();
    
            anim = GetComponent<Animator>();
    
            gravity = rb.gravityScale;
    
            if(Health == 0)
            {
                pState.alive = false;
                GameManager.Instance.RespawnPlayer();
            }
    
            //Health = maxHealth; Save之後不再需要
            //Mana = maxMana;
    
            healthImage.fillAmount = health / maxHealth;
            healthEffect.fillAmount = health / maxHealth;
            healthText.text = health.ToString() + "/" + maxHealth.ToString();
            manaImage.fillAmount = Mana;
    
            SaveData.Instance.LoadPlayerData();
        }
    
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireCube(SideAttackTransform.position, SideAttackArea);
            Gizmos.DrawWireCube(UpAttackTransform.position, UpAttackArea);
            Gizmos.DrawWireCube(DownAttackTransform.position, DownAttackArea);
            //Gizmos.DrawWireCube(wallCheck.position, )
        }
    
        void Update()
        {
            if (GameManager.Instance.gameIsPaused) return;
    
            if (pState.cutscene) return;
            if (pState.alive)
            {
                GetInputs();
                ToggleMap();
                ToggleInventory();
            }
            UpdateJumpVariables();
            RestoreTimeScale();
            UpdateCameraYDampForPlayerFall();
    
            if(pState.alive)
            {
                Heal();
            }
    
            if (pState.dashing || pState.healing) return;
    
            if(pState.alive)
            {
                if(!isWallJumping)
                {
                    Flip();
                    Move();
                    Jump();
                }
                if(unlockWallJump)
                {
                    WallSlide();
                    WallJump();
                }
                if(unlockDash)
                {
                    StartDash();
                }
                
    
                
                Attack();
                CastSpell();
            }
            FlashWhileInvincible();
        }
    
        private void FixedUpdate()
        {
            if (pState.cutscene) return;
    
            if (pState.dashing || pState.healing) return;
            Recoil();
        }
    
        void GetInputs() //獲取輸入
        {
            xAxis = Input.GetAxisRaw("Horizontal");
            yAxis = Input.GetAxisRaw("Vertical");
            attack = Input.GetButtonDown("Attack");
            openMap = Input.GetButton("Map");
            openInventory = Input.GetButton("Inventory");
    
            if (Input.GetButton("Cast/Heal"))
            {
                castOrHealtimer += Time.deltaTime;
            }
            /*
            else
            {
                castOrHealtimer = 0;
            }
            */
        }
    
        void ToggleMap()
        {
            if(openMap)
            {
                UIManager.Instance.mapHandler.SetActive(true);
            }
            else
            {
                UIManager.Instance.mapHandler.SetActive(false);
            }
        }
    
        void ToggleInventory()
        {
            if (openInventory)
            {
                UIManager.Instance.inventory.SetActive(true);
            }
            else
            {
                UIManager.Instance.inventory.SetActive(false);
            }
        }
    
        void Flip() //翻轉
        {
            if(xAxis < 0)
            {
                transform.localScale = new Vector2(-1, transform.localScale.y);
                pState.lookingRight = false;
            }
            else if(xAxis >0)
            {
                transform.localScale = new Vector2(1, transform.localScale.y);
                pState.lookingRight = true;
            }
        }
    
        private void Move() //移動
        {
            if (pState.healing) rb.velocity = new Vector2(0, 0); //如果玩家正在治療則無法移動
            rb.velocity = new Vector2(walkSpeed * xAxis ,rb.velocity.y);
            anim.SetBool("Walk", rb.velocity.x !=0 && Grounded());
        }
    
        void UpdateCameraYDampForPlayerFall()
        {
            //if falling past a certain speed threshold
            if (rb.velocity.y < playerFallSpeedTheshold && !CameraManager.Instance.isLerpingYDamp && !CameraManager.Instance.hasLerpingYDamping)
            {
                StartCoroutine(CameraManager.Instance.LerpYDamping(true));
            }
            //if standing stil or moveing up
            if(rb.velocity.y >= 0 && !CameraManager.Instance.isLerpingYDamp && CameraManager.Instance.hasLerpingYDamping)
            {
                //reset camera funtion
                CameraManager.Instance.hasLerpingYDamping = false;
                StartCoroutine(CameraManager.Instance.LerpYDamping(false));
            }
        }
    
        void StartDash() //衝刺
        {
            if(Input.GetButtonDown("Dash") && canDash && !dashed)
            {
                StartCoroutine(Dash());
                dashed = true;
            }
    
            if(Grounded())
            {
                dashed = false;
            }
        }
    
        IEnumerator Dash()
        {
            canDash = false; 
            pState.dashing = true;
            anim.SetTrigger("Dash");
            rb.gravityScale = 0;
            int _dir = pState.lookingRight ? 1 : -1;
            rb.velocity = new Vector2(_dir * dashSpeed, 0);
            yield return new WaitForSeconds(dashTime);
            rb.gravityScale = gravity;
            pState.dashing = false;
            yield return new WaitForSeconds(dashCooldown);
            canDash = true;
    
        }
    
        public IEnumerator WalkIntoNewScene(Vector2 _exitDir, float _delay) //過場動畫
        {
            pState.invincible = true;
    
            if(_exitDir.y > 0) //如果退出位置為向上
            {
                rb.velocity = jumpForce * _exitDir;
            }
    
            //如果退出位置為水平移動
            if(_exitDir.x != 0)
            {
                xAxis = _exitDir.x > 0 ? 1 : -1;
    
                Move();
            }
    
            Flip();
            yield return new WaitForSeconds(_delay);
            pState.invincible = false;
            pState.cutscene = false;
    
        }
    
        void Attack() //攻擊
        {
            timeSinceAttack += Time.deltaTime;
            if(attack && timeSinceAttack >= timeBetweenAttack)
            {
                timeSinceAttack = 0;
                anim.SetTrigger("Attack");
    
                if(yAxis == 0 || yAxis < 0 && Grounded()) //如果玩家在地面上且沒有按住"上",正常攻擊
                {
                    int _recoilLeftOrRight = pState.lookingRight ? 1 : -1; 
    
                    Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX, Vector2.right * _recoilLeftOrRight, recilXSpeed);
                    Instantiate(slashEffect, SideAttackTransform);
                }
                else if(yAxis > 0) //如果按住"上",上攻擊
                {
                    Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, Vector2.up, recilYSpeed);
                    SlashEffectAtAngle(slashEffect, 90, UpAttackTransform);
                }
                else if (yAxis < 0 && !Grounded()) //如果不在地面且按住"下",下攻擊
                {
                    Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, Vector2.down, recilYSpeed);
                    SlashEffectAtAngle(slashEffect, -90, DownAttackTransform);
                }
    
            }
        }
    
        private void Hit(Transform _attackTransfrom, Vector2 _attackArea, ref bool _recoilBool,Vector2 _recoilDir , float _recoilStrength) //給予傷害
        {
            Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransfrom.position, _attackArea, 0, attackableLayer);
            List<Enemy> hitEnemise = new List<Enemy>();
    
            if(objectsToHit.Length > 0)
            {
                //Debug.Log("Hit");
                _recoilBool = true;
    
            }
            for(int i = 0; i < objectsToHit.Length; i++)
            {
                if(objectsToHit[i].GetComponent<Enemy>() != null)
                {
                    objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, _recoilDir , _recoilStrength)  ;
                    /*
                    Enemy e = objectsToHit[i].GetComponent<Enemy>();
                    if(e && !hitEnemise.Contains(e))
                    {
                        e.EnemyHit(damage, (transform.position - objectsToHit[i].transform.position).normalized, _recoilStrength);
                        hitEnemise.Add(e);
                    }
                    */
                    if (objectsToHit[i].CompareTag("Monster")) //打到怪物增加魔力
                    {
                        Mana += manaGain;
                    }
                }
            }
        }
        
        void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle, Transform _attackTransform)
        {
            _slashEffect = Instantiate(_slashEffect, _attackTransform);
            _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
            _slashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y);
        }
    
        void Recoil() //反衝
        {
            if(pState.recoilingX)
            {
                if (pState.lookingRight)
                {
                    rb.velocity = new Vector2(-recilXSpeed, 0);
                }
                else
                {
                    rb.velocity = new Vector2(recilXSpeed, 0);
                }
            }
    
            if(pState.recoilingY)
            {
                rb.gravityScale = 0;
                if (yAxis < 0)
                {
                    rb.velocity = new Vector2(rb.velocity.x, recilYSpeed);
                }
                else
                {
                    rb.velocity = new Vector2(rb.velocity.x, -recilYSpeed);
                }
    
                airJumpCounter = 0;
    
            }
            else
            {
                rb.gravityScale = gravity;
            }
            
            //Stop recoil 停止反衝
            if(pState.recoilingX && stepsXRecoiled < recilXSteps)
            {
                stepsXRecoiled++;
            }
            else
            {
                StopRecoilX();
            }
    
            if (pState.recoilingY && stepsYRecoiled < recilYSteps)
            {
                stepsYRecoiled++;
            }
            else
            {
                StopRecoilY();
            }
    
            if(Grounded())
            {
                StopRecoilY();
            }
    
        }
    
        void StopRecoilX()
        {
            stepsXRecoiled = 0;
            pState.recoilingX = false;
        }
        void StopRecoilY()
        {
            stepsYRecoiled = 0;
            pState.recoilingY = false;
        }
    
        public void TakeDamage(float _damage) //玩家受傷
        {
            if(pState.alive)
            {
                Health -= Mathf.RoundToInt(_damage);
                if(Health <= 0)
                {
                    Health = 0;
                    StartCoroutine(Death());
                }
                else
                {
                    StartCoroutine(StopTakingDamage());
    
                    healthImage.fillAmount = health / maxHealth; //血量圖片
                    healthText.text = health.ToString() + "/" + maxHealth.ToString(); //血量文字
    
                    if (updateCoroutine != null)
                    {
                        StopCoroutine(updateCoroutine);
                    }
                    updateCoroutine = StartCoroutine(UpdateHealthEffect());
                } 
            }
        }
    
        private IEnumerator UpdateHealthEffect() //血量緩降協程
        {
            float effectLength = healthEffect.fillAmount - healthImage.fillAmount;
            float elapsedTime = 0f;
    
            while (elapsedTime < effectTime && effectLength != 0)
            {
                elapsedTime += Time.deltaTime;
                healthEffect.fillAmount = Mathf.Lerp
                (healthImage.fillAmount + effectLength, healthImage.fillAmount, elapsedTime / effectTime);
                yield return null;
            }
            healthEffect.fillAmount = healthImage.fillAmount;
        }
    
        IEnumerator StopTakingDamage() //無敵偵
        {
            pState.invincible = true;
            GameObject _hitEffect = Instantiate(hitEffect, transform.position, Quaternion.identity);
            Destroy(_hitEffect, 1.5f);
            anim.SetTrigger("Hurt");
            yield return new WaitForSeconds(1f);
            pState.invincible = false;
        }
    
        IEnumerator Flash()
        {
            sr.enabled = !sr.enabled;
            canFlash = false;
            yield return new WaitForSeconds(0.2f);
            canFlash = true;
        }    
    
        void FlashWhileInvincible()
        {
            //將渲染改為"PingPongLerp" 如果玩家是無敵狀態則將其從白色改為黑色 否則將玩家設定為白色
            //sr.material.color = pState.invincible ? Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashSpeed, 1.0f)) : Color.white;
            if(pState.invincible && !pState.cutscene)
            {
                if (Time.timeScale > 0.2f && canFlash) 
                {
                    StartCoroutine(Flash()); 
                }
            }
            else
            {
                sr.enabled = true;
            }
        }
    
        void RestoreTimeScale() //受傷暫停時間
        {
            if(restoreTime) //檢查是否為真
            {
                if(Time.timeScale < 1) //如果小於1 時間速度加快到達1
                {
                    Time.timeScale += Time.unscaledDeltaTime * restoreTimeSpeed;
                }
                else //如果超過1 時間為1
                {
                    Time.timeScale = 1;
                    restoreTime = false;
                }
            }
        }
    
        public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay)
        {
            restoreTimeSpeed = _restoreSpeed;
            Time.timeScale = _newTimeScale;
    
            if(_delay > 0)
            {
                StopCoroutine(StartTimeAgain(_delay));
                StartCoroutine(StartTimeAgain(_delay));
            }
            else
            {
                restoreTime = true;
            }
        }
    
        IEnumerator StartTimeAgain(float _delay)
        {
            yield return new WaitForSecondsRealtime(_delay);
            restoreTime = true;
        }
    
        IEnumerator Death()
        {
            pState.alive = false;
            Time.timeScale = 1f; //避免受傷暫停時間導致出錯
            //可在此呼叫粒子效果
            anim.SetTrigger("Death");
    
            rb.constraints = RigidbodyConstraints2D.FreezePosition;
            rb.constraints = RigidbodyConstraints2D.FreezeRotation;
            GetComponent<BoxCollider2D>().enabled = false;
    
            yield return new WaitForSeconds(0.9f);
            StartCoroutine(UIManager.Instance.ActiveDeathScreen());
        }
    
        public void Respawned()
        {
            if(!pState.alive)
            {
                rb.constraints = RigidbodyConstraints2D.None;
                rb.constraints = RigidbodyConstraints2D.FreezeRotation;
                GetComponent<BoxCollider2D>().enabled = true;
    
                pState.alive = true;
                Health = maxHealth;
                anim.Play("PlayerIdle");
            }
        }
    
        public float Health //血量
        {
            get { return health; }
            set
            {
                if(health != value)
                {
                    health = Mathf.Clamp(value, 0, maxHealth);
    
                }
            }
        }
    
        void Heal()
        {
            if(Input.GetButton("Cast/Heal") && castOrHealtimer > 0.5f && health < maxHealth && Mana > 0 && Grounded() && !pState.dashing)
            {
                pState.healing = true;
                anim.SetBool("Heal", true);
    
                //補血
                healTimer += Time.deltaTime;
                if(healTimer >= timeToheal)
                { 
                    health++; //恢復血量
                    healTimer = 0; //血量計時器
                    healthImage.fillAmount = health / maxHealth; //血量圖片
                    healthEffect.fillAmount = health / maxHealth; //血量效果圖片
                    healthText.text = health.ToString() + "/" + maxHealth.ToString(); //血量文字
                }
                Mana -= Time.deltaTime * manaDrainSpeed;//魔力消耗
            }
            else
            {
                pState.healing = false;
                anim.SetBool("Heal", false);
                healTimer = 0;
            }
    
            
        }
    
        public float Mana
        {
            get { return mana; }
            set
            {
                if(mana != value)
                {
                    mana = Mathf.Clamp(value, 0, maxMana);
                    manaImage.fillAmount = Mana;
                }
    
            }
        }
    
        void CastSpell()
        {
            if(Input.GetButtonUp("Cast/Heal") && castOrHealtimer <= 0.5f && timeSinceCast >= timeBetweenCast && Mana > manaSpellCost)
            {
                pState.casting = true;
                timeSinceCast = 0;
                StartCoroutine(CastCoroutine());
            }
            else
            {
                timeSinceCast += Time.deltaTime;
            }
            
            if(!Input.GetButton("Cast/Heal"))
            {
                castOrHealtimer = 0;
            }
            
        }
    
        IEnumerator CastCoroutine()
        {
            //橫向技能
            if((yAxis == 0 || (yAxis < 0 && Grounded())) && unlockSideSpell ) //沒有垂直輸入時播放 目前無需要按住上下才能施放的技能
            {
                anim.SetBool("Cast", true);
                yield return new WaitForSeconds(0.1f); //此為等待動畫到達正確播放時間 
    
                GameObject _fireBall = Instantiate(sideSpellFireball, SideAttackTransform.position, Quaternion.identity); //實例化該物件
    
                //Flip火球
                if(pState.lookingRight)
                {
                    _fireBall.transform.eulerAngles = Vector3.zero;
                }
                else
                {
                    _fireBall.transform.eulerAngles = new Vector2(_fireBall.transform.eulerAngles.x, 180); //如果不是面向右邊翻轉180度
                }
                pState.recoilingX = true; //給予玩家反衝
    
                Mana -= manaSpellCost; //魔力減少
                yield return new WaitForSeconds(0.3f); //此為動畫撥放完整時間
            }
    
            anim.SetBool("Cast", false);
            pState.casting = false;
    
        }
    
        public bool Grounded() //地面檢測
        {
            if (Physics2D.Raycast(groundCheckPoint.position, Vector2.down, groundCheckY, whatIsGround) 
                || Physics2D.Raycast(groundCheckPoint.position + new Vector3(groundCheckX , 0, 0), Vector2.down, groundCheckY, whatIsGround)
                || Physics2D.Raycast(groundCheckPoint.position + new Vector3(-groundCheckX , 0, 0), Vector2.down, groundCheckY, whatIsGround))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        
        void Jump() //跳躍
        {
            if (jumpBufferCounter > 0 && coyoteTimeCounter > 0 && !pState.jumping)
            {
                
                rb.velocity = new Vector3(rb.velocity.x, jumpForce);
    
                pState.jumping = true;
            }
    
            if (!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump") && unlockAirJump && !isWallSliding)
            {
                
                pState.jumping = true;
    
                airJumpCounter++;
    
                rb.velocity = new Vector3(rb.velocity.x, jumpForce);
            }
    
            if (Input.GetButtonUp("Jump") && rb.velocity.y > 3)
            {
                rb.velocity = new Vector2(rb.velocity.x, 0);
    
                pState.jumping = false;
            }
    
            anim.SetBool("Jump", !Grounded());
        }
        
    
        void UpdateJumpVariables()
        {
            if(Grounded())
            {
                pState.jumping = false;
                coyoteTimeCounter = coyoteTime;
                airJumpCounter = 0;  
            }
            else
            {
                coyoteTimeCounter -= Time.deltaTime;
            }
    
            if(Input.GetButtonDown("Jump") )
            {
                jumpBufferCounter = jumpBufferFrames;
            }
            else
            {
                jumpBufferCounter--;
            }
    
        }
    
        private bool Walled()
        {
            return Physics2D.OverlapCircle(wallCheck.position, 0.2f, wallLayer);
        }
    
        void WallSlide()
        {
            if(Walled() && !Grounded() && xAxis !=0)
            {
                isWallSliding = true;
                rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue));
            }
            else
            {
                isWallSliding = false;
            }
        }
    
        void WallJump()
        {
            if(isWallSliding)
            {
                isWallJumping = false;
                wallJumpingDirection = !pState.lookingRight ? 1 : -1;
    
                CancelInvoke(nameof(StopWallJumping));
            }
    
            if(Input.GetButtonDown("Jump") && isWallSliding)
            {
                isWallJumping = true;
                rb.velocity = new Vector2(wallJumpingDirection * wallJumpPower.x, wallJumpPower.y);
    
                dashed = false;
                airJumpCounter = 0;
    
                pState.lookingRight = !pState.lookingRight;
                transform.eulerAngles = new Vector2(transform.eulerAngles.x, 180);
    
                Invoke(nameof(StopWallJumping), wallJumpingDuration);
            }
        }
    
        void StopWallJumping()
        {
            isWallJumping = false;
            transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0);
        }
    
    }
    
Viewing 15 posts - 1 through 15 (of 18 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: