Forum begins after the advertisement:


[part 10] Boss do not dead

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [part 10] Boss do not dead

Viewing 14 posts - 1 through 14 (of 14 total)
  • Author
    Posts
  • #16688
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    Hello guys After checking and testing, the boss was could dead before, but then when i decide to make the boss door in the part 10, I checked again and now the boss doesn’t dead anymore as you can see as the image below, i already hit the boss to -4 hp but the boss still walking and hitting me

    Boss do not dead The boss still can hit me and i can hit the boss as well I had already tried that remove the spawn script and put the boss to the room from the start but the boss still doesn’t dead after the health reduce to 0 here the boss code
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CorruptedMonk : Enemy
    {
        public static CorruptedMonk Instance;
    
        [SerializeField] private AudioClip changestateSound;
    
        [Header("Attacking setting")]
        [SerializeField] public Transform sideAttackTransform, upAttackTransform, downAttackTransform;
        [SerializeField] public Vector2 sideAttackArea, upAttackArea, downAttackArea;
        [Space(5)]
    
        public float attackRange;
        public float attackTimer;
    
        [Header("Groundcheck setting")]
        [SerializeField] private Transform groundcheck;
        [SerializeField] private Transform wallCheck;
        [SerializeField] private float groundchecky = 0.2f;
        [SerializeField] private float groundcheckx = 0.5f;
        [SerializeField] private LayerMask isground;
    
        int hitCounter;
        bool stunned, canStunned;
        bool alive;
        public bool damagedPlayer = false;
    
        [HideInInspector] public float runSpeed;
        [HideInInspector] public bool facingRight;
        [HideInInspector] public bool parrying;
        private void Awake()
        {
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
        }
        // Start is called before the first frame update
        protected override void Start()
        {
            base.Start();
            Sr = GetComponentInChildren<SpriteRenderer>();
            anim = GetComponentInChildren<Animator>();
            ChangeState(EnemyStates.Monk_State1);
            alive = true;
        }
    
        public bool grounded;
        public bool isonGround()
        {
            if (Physics2D.Raycast(groundcheck.position, Vector2.down, groundchecky, isground)
                || Physics2D.Raycast(groundcheck.position + new Vector3(groundcheckx, 0, 0), Vector2.down, groundchecky, isground)
                || Physics2D.Raycast(groundcheck.position + new Vector3(-groundcheckx, 0, 0), Vector2.down, groundchecky, isground)
                )
            {
                grounded = true;
                return true;
            }
            else
            {
                grounded = false;
                return false;
            }
        }
    
        public bool touchWall()
        {
            if (Physics2D.Raycast(wallCheck.position, Vector2.down, groundchecky, isground)
                || Physics2D.Raycast(wallCheck.position + new Vector3(groundcheckx, 0, 0), Vector2.down, groundchecky, isground)
                || Physics2D.Raycast(wallCheck.position + new Vector3(-groundcheckx, 0, 0), Vector2.down, groundchecky, isground)
                )
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireCube(sideAttackTransform.position, sideAttackArea);
            Gizmos.DrawWireCube(upAttackTransform.position, upAttackArea);
            Gizmos.DrawWireCube(downAttackTransform.position, downAttackArea);
        }
        // Update is called once per frame
        protected override void Update()
        {
            base.Update();
    
            if(health <= 0 && alive)
            {
                Death(0);
            }
    
            if (!attacking)
            {
                attackCountdown -= Time.deltaTime;
            }
            if (stunned)
            {
                rb.velocity = Vector2.zero;
            }
        }
    
        public void Flip()
        {
            if (Move.Instance.transform.position.x < transform.position.x && transform.localScale.x > 0)
            {
                transform.eulerAngles = new Vector2(transform.eulerAngles.x, 180);
                facingRight = false;
            }
            else
            {
                transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0);
                facingRight = true;
            }
        }
    
    
        protected override void UpdateEnemyStates()
        {
            if(Move.Instance != null)
            {
                switch (GetCurrentEnemyStates)
                {
                    case EnemyStates.Monk_State1:
                        canStunned = true;
                        attackTimer = 4;
                        runSpeed = speed;
                        dmgMake = 2;
                        break;
                    case EnemyStates.Monk_State2:
                        new WaitForSeconds(1f);
                        anim.SetBool("Changestate", false);
                        audioSource.PlayOneShot(changestateSound);
                        new WaitForSeconds(1f);
                        canStunned = true;
                        attackTimer = 2;
                        runSpeed = speed * 1.1f;
                        dmgMake = 3;
                        break;
                }
            } 
        }
    
        protected override void OnCollisionStay2D(Collision2D _other)
        {
    
        }
    
        public void ResetAllAttack()
        {
            attacking = false;
            StopCoroutine(TriplePunch());
            StopCoroutine(SweepKick());
            StopCoroutine(Parry());
            StopCoroutine(payback());
    
            flyingKichAttack = false;
        }
    
        #region attacking
        #region variables
        [HideInInspector] public bool attacking;
        [HideInInspector] public float attackCountdown;
        [HideInInspector] public Vector2 moveToPosition;
        [HideInInspector] public bool flyingKichAttack;
        public GameObject divingCollider;
        public GameObject pillar;
        #endregion
    
        #region Control
    
        public void AttackHandler()
        {
            if(currentState == EnemyStates.Monk_State1)
            {
                if(Vector2.Distance(Move.Instance.transform.position,rb.position) <= attackRange)
                {
                    StartCoroutine(TriplePunch());
                }
                else
                {
                    StartCoroutine(SweepKick());
                }
            }
            if (currentState == EnemyStates.Monk_State2)
            {
                if (Vector2.Distance(Move.Instance.transform.position, rb.position) <= attackRange)
                {
                    StartCoroutine(TriplePunch());
                }
                else
                {
                    int attackChoose = Random.Range(1,2);
                    if(attackChoose == 1){
                        StartCoroutine(SweepKick());
                    }
                    if (attackChoose == 2)
                    {
                        FlyingKickAttackJump();
                    }
                }
            }
        }
    
        #endregion
    
        #region Stage1
        IEnumerator TriplePunch()
        {
            attacking = true;
            rb.velocity = Vector2.zero;
            anim.SetTrigger("Punch");
            Debug.Log("First punch");
            yield return new WaitForSeconds(1f);
            anim.ResetTrigger("Punch");
    
    
            anim.SetTrigger("Punch");
            Debug.Log("Second punch");
            yield return new WaitForSeconds(0.7f);
            anim.ResetTrigger("Punch");
    
    
            anim.SetTrigger("Punch");
            Debug.Log("Third punch");
            yield return new WaitForSeconds(0.3f);
            anim.ResetTrigger("Punch");
    
            ResetAllAttack();
        }
    
        IEnumerator SweepKick()
        {
            Flip();
            attacking = true ;
            Debug.Log("Kich start");
            anim.SetBool("Kick",true);
            yield return new WaitForSeconds(1f);
            Debug.Log("Kich hit");
            anim.SetBool("Kick", false);
            damagedPlayer = false;
            ResetAllAttack() ;
        }
    
        IEnumerator Parry()
        {
            attacking=true ;
            rb.velocity = Vector2.zero;
            Debug.Log("start Parry");
            anim.SetBool("Parry", true);
            yield return new WaitForSeconds(0.8f);
            anim.SetBool("Parry", false);
    
            parrying = false ;
            ResetAllAttack();
        }
    
        #endregion
        #endregion
    
        public override void EnemyHit(float _DmgDone, Vector2 _hitDirection, float _hitForce)
        {
    
            if (!stunned)
            {
    
    
                if (!parrying)
                {
                    if (canStunned)
                    {
                        hitCounter++;
                        if(hitCounter >= 6)
                        {
                            ResetAllAttack();
                            StartCoroutine(Stunned());
                        }
                    }
                    base.EnemyHit(_DmgDone, _hitDirection, _hitForce);
                    StartCoroutine(Parry());
                }
                else
                {
                    StopCoroutine(Parry());
                    parrying=false;
                    ResetAllAttack();
                    StartCoroutine(payback());
                }
            }
            else
            {
                StopCoroutine(Stunned());
                anim.SetBool("Stun", false );
                stunned = false;
            }
            if(health > 30)
            {
                ChangeState(EnemyStates.Monk_State1);
            }
            if(health <= 15)
            {
                anim.SetBool("Changestate", true);
                ChangeState(EnemyStates.Monk_State2);
            }
    
            if(health <= 0 && alive)
            {
                Death(0);
            }
        }
    
        IEnumerator payback()
        {
            attacking = true;
            rb.velocity = Vector2.zero;
            anim.SetTrigger("Punch");
            yield return new WaitForSeconds(0.5f);
            anim.ResetTrigger("Punch");
    
            ResetAllAttack();
        }
    
        #region Stage2
    
        void FlyingKickAttackJump()
        {
            attacking = true ;
            moveToPosition = new Vector2(Move.Instance.transform.position.x, rb.velocity.y +2);
            flyingKichAttack = true ;
            anim.SetBool("Jump", true);
        }
    
        public void Dive()
        {
            anim.SetBool("FlyingKick",true);
            anim.SetBool("Jump", false);
        }
    
        private void OnTriggerEnter2D(Collider2D collision)
        {
            if (collision.GetComponent<Move>() != null && flyingKichAttack)
            {
                collision.GetComponent<Move>().takeDmg(dmgMake * 2);
                Move.Instance.playerStateList.recoilingX = true;
    
                // Disable the attack to prevent further damage
                flyingKichAttack = false;
            }
        }
    
    
        public void DivingPillar()
        {
            Vector2 _impactPoint = groundcheck.position;
            float _spawnDistance = 5f;
    
            for (int i = 0; i < 5; i++)
            {
                Vector2 _pillarSpawnPointRight = _impactPoint + new Vector2(_spawnDistance, 0);
                Vector2 _pillarSpawnPointLeft = _impactPoint - new Vector2(_spawnDistance, 0);
                Debug.Log($"Diving Pillar {i} has a spawn distance of {_spawnDistance}");
                Debug.Log($"Spawning right pillar at: {_pillarSpawnPointRight}");
                Instantiate(pillar, _pillarSpawnPointRight, Quaternion.Euler(0, 0, 0));
                Debug.Log($"Spawning left pillar at: {_pillarSpawnPointLeft}");
                Instantiate(pillar, _pillarSpawnPointLeft, Quaternion.Euler(0, 0, 0));
                Debug.DrawLine(_impactPoint, _pillarSpawnPointRight, Color.red, 5f);
                Debug.DrawLine(_impactPoint, _pillarSpawnPointLeft, Color.blue, 5f);
    
                _spawnDistance += 2;
            }
    
            ResetAllAttack();
        }
    
        #endregion
    
        public IEnumerator Stunned()
        {
            stunned = true;
            hitCounter = 0;
            anim.SetBool("Stun", true);
    
            yield return new WaitForSeconds(4f);
            anim.SetBool("Stun", false);
            stunned = false;
        }
    
        protected override void Death(float _destroyTime)
        {
            ResetAllAttack();
            alive = false;
            rb.velocity = new Vector2(rb.velocity.x, -25);
            anim.SetTrigger("Die");
        }
    
        public void DestroyAfterDeath()
        {
            Destroy(gameObject);
        }
    }
    

    I’m sure that i do not touching anything to this code when i made the boss door. I will give the boss door script too

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SpawnBoss : MonoBehaviour
    {
        // Start is called before the first frame update
    
        public static SpawnBoss Instance;
        [SerializeField] Transform spawnPoint;
        [SerializeField] GameObject boss;
        [SerializeField] Vector2 exitDir;
        bool callOnce;
        BoxCollider2D col;
    
        private void Awake()
        {
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
        }
        void Start()
        {
            col = GetComponent<BoxCollider2D>();
        }
    
        // Update is called once per frame
        void Update()
        {
    
        }
        private void OnTriggerEnter2D(Collider2D collision)
        {
            if (collision.CompareTag("Player"))
            {
                if (!callOnce)
                {
                    StartCoroutine(WalktoRoom());
                    callOnce = true;
                }
            }
        }
    
        IEnumerator WalktoRoom()
        {
            StartCoroutine(Move.Instance.WalktonewScene(exitDir, 1));
            Move.Instance.playerStateList.incutscene = true;
            yield return new WaitForSeconds(1f);
            col.isTrigger = false;
            Instantiate(boss, spawnPoint.position, Quaternion.identity);
        }
    
        public void isnotTrigger()
        {
            col.isTrigger = true;
        }
    }
    

    And as the video of part 10 i add the boss spawn into the monk event script

        void DestroyAfterDeath()
        {
            SpawnBoss.Instance.isnotTrigger();
            CorruptedMonk.Instance.DestroyAfterDeath();
        }
    

    Please help me to fix this. Tks you guys

    #16691
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    Ok so for some reason, I manage to fix the boss death with the help of GPT, but the boss door doesn’t change it state into trigger again and my character got stuck in the boss room and cannot get out AS you can see firstly in the console, the boss is dead

    console show boss death

    but here the boss door still off the trigger

    boss door status and for some reason, after defeated the boss, the game didn’t save the boss status either save folder

    i already have the debug in the monk event script but as you can see at the console picture, the boss doesn’t got save

    
    void DestroyAfterDeath()
       {
           SpawnBoss.Instance.isnotTrigger();
           CorruptedMonk.Instance.DestroyAfterDeath();
           GameManager.Instance.bossDeafeated = true;
           SaveData.Instance.saveBoss();
           Debug.Log("Save boss data");
           SaveData.Instance.savePlayerData();
       }
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.SceneManagement;
    
    [System.Serializable]
    public struct SaveData
    {
        //player
        public static SaveData Instance;
       public HashSet<string> sceneNames;
        public string altersceneName;
        public Vector2 alterPos;
        public int playerHealth;
        public int playerHeartShards;
        public float playerMana;
        public Vector2 playerPosition;
        public string lastScene;
        public int playerMaxHealth;
    
    
        public bool playerUnlockWallJump;
        public bool playerUnlockDash;
        public bool playerUnlockDbJump;
    
        //boss
        public bool BossDeafeated;
        public void Initialize()
        {
            if (!File.Exists(Application.persistentDataPath + "/save.alter.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.alter.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.boss.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.boss.data"));
            }
            if (sceneNames == null)
            {
                sceneNames = new HashSet<string>();
            }
        }
    
        public void saveAlter()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.alter.data")))
            {
                writer.Write(altersceneName);
                writer.Write(alterPos.x);
                writer.Write(alterPos.y);
                Debug.Log("saved Max Health: " + playerMaxHealth);
                Debug.Log("saved heart shards: " + playerHeartShards);
            }
        }
        public void loadAlter()
        {
            string savePath = Application.persistentDataPath + "/save.alter.data";
            if (File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.alter.data")))
                {
                    altersceneName = reader.ReadString();
                    alterPos.x = reader.ReadSingle();
                    alterPos.y = reader.ReadSingle();
                    Debug.Log("saved Max Health: " + playerMaxHealth);
                    Debug.Log("saved heart shards: " + playerHeartShards);
                }
            }
        }
        public void savePlayerData()
        {
            string filePath = Application.persistentDataPath + "/save.player.data";
            using (BinaryWriter writer = new BinaryWriter(File.Create(filePath)))
            {
                // Save player health and max health
                playerHealth = Move.Instance.Health;
                writer.Write(playerHealth);
    
                playerMaxHealth = Move.Instance.MaxHealth;
                writer.Write(playerMaxHealth);
    
                // Save player mana
                playerMana = Move.Instance.Mana;
                writer.Write(playerMana);
    
                // Save player unlock abilities
                playerUnlockWallJump = Move.Instance.unlockWalljump;
                writer.Write(playerUnlockWallJump);
    
                playerUnlockDash = Move.Instance.unlockDash;
                writer.Write(playerUnlockDash);
    
                playerUnlockDbJump = Move.Instance.unlockDbJump;
                writer.Write(playerUnlockDbJump);
    
                // Save player position
                playerPosition = Move.Instance.transform.position;
                writer.Write(playerPosition.x);
                writer.Write(playerPosition.y);
    
                // Save player heart shards
                playerHeartShards = Move.Instance.heartShards;
                writer.Write(playerHeartShards);
    
                // Save the last scene name
                lastScene = SceneManager.GetActiveScene().name;
                writer.Write(lastScene);
    
                Debug.Log("Player data saved successfully.");
                Debug.Log("Before saving - Health: " + playerHealth + ", Max Health: " + playerMaxHealth + ", Heart Shards: " + playerHeartShards);
            }
        }
    
        public void loadPlayerData()
        {
            string filePath = Application.persistentDataPath + "/save.player.data";
            if (File.Exists(filePath))
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath)))
                {
                    // Load all necessary data
                    playerHealth = reader.ReadInt32();
                    playerMaxHealth = reader.ReadInt32();
                    playerMana = reader.ReadSingle();
                    playerUnlockWallJump = reader.ReadBoolean();
                    playerUnlockDash = reader.ReadBoolean();
                    playerUnlockDbJump = reader.ReadBoolean();
                    playerPosition.x = reader.ReadSingle();
                    playerPosition.y = reader.ReadSingle();
                    playerHeartShards = reader.ReadInt32();
                    lastScene = reader.ReadString();
    
                    Debug.Log($"Loaded Health: {playerHealth}, Max Health: {playerMaxHealth}");
    
                    // Load the scene and register a callback to set player data after the scene loads
                    SceneManager.sceneLoaded += OnSceneLoaded;
                    SceneManager.LoadScene(lastScene);
                }
            }
            else
            {
                Debug.Log("Save file not found. Initializing default values.");
                Move.Instance.Health = Move.Instance.MaxHealth;
                Move.Instance.heartShards = 0;
                Move.Instance.Mana = 0.5f;
                Move.Instance.unlockWalljump = false;
                Move.Instance.unlockDash = false;
                Move.Instance.unlockDbJump = false;
            }
        }
    
        // Callback function to set player data after the scene has loaded
        private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            // Unregister the event to avoid it being called multiple times
            SceneManager.sceneLoaded -= OnSceneLoaded;
    
            // Set the player's data once the scene has fully loaded
            Move.Instance.transform.position = playerPosition;
            Move.Instance.MaxHealth = playerMaxHealth;
            Move.Instance.Health = playerHealth;
            Move.Instance.Mana = playerMana;
            Move.Instance.unlockWalljump = playerUnlockWallJump;
            Move.Instance.unlockDash = playerUnlockDash;
            Move.Instance.unlockDbJump = playerUnlockDbJump;
            Move.Instance.heartShards = playerHeartShards;
    
            Debug.Log($"After loading - Health: {Move.Instance.Health}, Max Health: {Move.Instance.MaxHealth}, Position: {playerPosition}");
        }
    
        public void saveBoss()
        {
            if (!File.Exists(Application.persistentDataPath + "/save.boss.data")) //if file doesnt exist, well create the file
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.boss.data"));
            }
    
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.boss.data")))
            {
                BossDeafeated = GameManager.Instance.bossDeafeated;
                writer.Write(BossDeafeated);
            }
        }
        public void loadBoss()
        {
            string savePath = Application.persistentDataPath + "/save.boss.data";
            if (File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.boss.data")))
                {
                    BossDeafeated = reader.ReadBoolean();
                    GameManager.Instance.bossDeafeated = BossDeafeated;
                }
            }
        }
    }
    
    #16697
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Are there any errors in your Console when the boss dies?

    Add this to the Awake() function of your SpawnBoss script and show me your console when the boss dies:

        private void Awake()
        {
            if(Instance != null && Instance != this)
            {
                Debug.LogWarning("Multiple SpawnBoss scripts found!");
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
        }
    #16699
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    So there is no errors messages appear in the console when the boss die, only some warning about the sound clip is null

    console i had already change the spawn boss code as you said
    private void Awake()
    {
        if(CorruptedMonk.Instance != null)
        {
            Destroy(CorruptedMonk.Instance);
            callOnce = false;
            col.isTrigger = true;
        }
    
        if(GameManager.Instance.bossDeafeated)
        {
            callOnce = true;
        }
    
        if(Instance != null && Instance != this)
        {
            Debug.LogWarning("Multiple SpawnBoss scripts found!");
            Destroy(gameObject);
        }
        else
        {
            Instance = this;
        }
    }

    and the moment the boss is spawn, nothing happend either

    boss spawn
    #16700
    A_DONUT
    Level 6
    Moderator
    Helpful?
    Up
    0
    ::

    Is ur doubt already solved?

    #16701
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    It kinda hard to said that it was done. I solved it but it just a temporary method. By adding some code into the save boss and boss death like this

    public void saveBoss()
    {
        string savePath = Application.persistentDataPath + "/save.boss.data";
    
        // Ensure the save file is created and written properly
        using (BinaryWriter writer = new BinaryWriter(File.Create(savePath)))
        {
            BossDeafeated = GameManager.Instance.bossDeafeated; // Get the defeat state
            writer.Write(BossDeafeated); // Write the state to the file
        }
    
        Debug.Log("Boss save file created and state saved: " + BossDeafeated);
    }
     protected override void Death(float _destroyTime)
     {
         if (!alive) return; // Prevent multiple death calls
    
         Debug.Log("Boss is dead!");
         alive = false;
         ResetAllAttack(); // Ensure attacks are stopped
    
         rb.velocity = Vector2.zero; // Stop movement
         anim.SetTrigger("Die"); // Play death animation
    
         // Optionally disable collider or make the boss "inactive"
         GetComponent<Collider2D>().enabled = false;
         GameManager.Instance.bossDeafeated = true;
         SaveData.Instance.saveBoss();
    
         // Destroy object after delay
         Destroy(gameObject, _destroyTime);
     }

    the game could said it work by the boss save file in the save game folder. But the game didn’t save it automatically. I need to press p to save it by this code in game manager

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.P))
        {
            SaveData.Instance.savePlayerData();
        }
        if (Input.GetKeyDown(KeyCode.Escape) && !gameIsPause)
        {
            pauseMenu.fadeUIIn(fadeTime);
            Debug.Log("Pausing game. GameManager active: " + this.gameObject.activeSelf);
            Time.timeScale = 0;
            gameIsPause = true;
        }
    }

    I would tried to deploy the old code to see is it made any difference. Update: the old way still work but for some reason, the door still stuck and do not set trigger again after i come and defeated the boss Another update: Old way alway make an error is Out of Stream. When i change back to the new way, the error is disapear and i see that if i turn of the game and turn on again, the door of the boss room is set to trigger again and player could go through it normally with out making it spawn the boss again.

    #16704
    A_DONUT
    Level 6
    Moderator
    Helpful?
    Up
    0
    ::

    Your new approach appears to handle saving the boss’s defeated state more effectively, and it also avoids the “Out of Stream” error seen with the old method. It seems like your solution resolves the immediate issue with the boss’s door being triggered properly after a game restart. Here’s a breakdown of what you’ve done well and areas that could use improvement:

    Key Observations:

    1. Persistent Data Path:

      • You’re saving the boss state in the Application.persistentDataPath. This is the correct approach for ensuring the save data persists across sessions.
    2. Improved Save Logic:

      • Writing to a file using a BinaryWriter ensures the save process is robust.
      • The inclusion of GameManager.Instance.bossDeafeated ensures the state is centralized and correctly tracked.
    3. Boss Death and Save Call:

      • Automatically saving the boss state upon its death (SaveData.Instance.saveBoss()) ensures the state is updated immediately.
      • Stopping the boss’s movement, resetting attacks, and playing a death animation are all logical steps in the Death method.
    4. Manual Save via Key Press:

      • Using Input.GetKeyDown(KeyCode.P) to save is a good temporary solution, but not ideal for long-term usability.

    Suggestions for Improvement:

    1. Automate Saving:

      • Instead of relying on pressing P to save manually, you can integrate an autosave system triggered by significant game events, such as defeating a boss or passing a checkpoint. For example:
      public void TriggerAutoSave()
      {
          SaveData.Instance.savePlayerData();
          Debug.Log("Game autosaved.");
      }
    2. Separate Save Logic:

      • Consider separating the save logic for the boss and the door. If the door’s state is dependent on the boss’s state, ensure this is handled during game load:
      public void LoadGameState()
      {
          string savePath = Application.persistentDataPath + "/save.boss.data";
      
          if (File.Exists(savePath))
          {
              using (BinaryReader reader = new BinaryReader(File.OpenRead(savePath)))
              {
                  GameManager.Instance.bossDeafeated = reader.ReadBoolean();
                  Debug.Log("Loaded boss state: " + GameManager.Instance.bossDeafeated);
              }
          }
      }
    3. Debugging the Door Issue:

      • Ensure the door checks the boss’s state during game initialization. For example:
      void UpdateDoorState()
      {
          if (GameManager.Instance.bossDeafeated)
          {
              door.SetTrigger("Open");
              Debug.Log("Door is set to open.");
          }
          else
          {
              Debug.Log("Door remains locked.");
          }
      }

      Call this method during the level’s start or after loading the game state.

    4. Optimize the Update Method:

      • Your Update method could become bloated if many conditions are checked every frame. Consider event-driven triggers instead, e.g., using Unity Events or custom events to manage pauses and saves.
    5. File I/O Error Handling:

      • Add error handling to your save and load logic to prevent crashes or data corruption in edge cases:
        try
        {
          using (BinaryWriter writer = new BinaryWriter(File.Create(savePath)))
          {
              writer.Write(BossDeafeated);
          }
        }
        catch (Exception ex)
        {
          Debug.LogError("Failed to save boss data: " + ex.Message);
        }

    Conclusion:

    Your current implementation resolves several issues effectively, but automating saves and debugging the door logic would further improve the user experience. With these refinements, your game will have a more robust save system and seamless state management.

    #16705
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    2
    ::

    @pepecry can I see the errors on the Console for the old way that you used?

    and 1 other person have upvoted this post.
    #16706
    A_DONUT
    Level 6
    Moderator
    Helpful?
    Up
    0
    ::

    Your current setup saves the boss’s defeat properly and fixes the “Out of Stream” error. The save triggers automatically when the boss dies, and the door works fine after restarting the game. However, needing to press P for manual saves isn’t ideal.

    To improve, add an autosave feature for key events like defeating a boss. Also, ensure the door checks the boss’s state during game load to update correctly. For smoother performance, replace constant checks in Update with event-based triggers. Adding error handling will make the save/load system more reliable and user-friendly.

    And as sir said above can you please provide the console error for the old way?

    #16709
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    I got some mistake here, the error is not Out of Stream error. So if i tried this code in the save boss function which is the old way to reproduce the error.

    public void saveBoss()
    {
        if (!File.Exists(Application.persistentDataPath + "/save.boss.data")) //if file doesnt exist, well create the file
        {
            BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.boss.data"));
        }
    
        // Ensure the save file is created and written properly
        using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.boss.data")))
        {
            BossDeafeated = GameManager.Instance.bossDeafeated; // Get the defeat state
            writer.Write(BossDeafeated); // Write the state to the file
        }
    
        Debug.Log("Boss save file created and state saved: " + BossDeafeated);
    }
    

    The error appear like this

    console error As you could see, the boss must be death, the death function has been called in the console but the boss didn’t destroy. He can still hit me, using hit skill and i can hit him either.
    #16710
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    But if i using this way

     public void saveBoss()
     {
         string savePath = Application.persistentDataPath + "/save.boss.data";
    
         // Ensure the save file is created and written properly
         using (BinaryWriter writer = new BinaryWriter(File.Create(savePath)))
         {
             BossDeafeated = GameManager.Instance.bossDeafeated; // Get the defeat state
             writer.Write(BossDeafeated); // Write the state to the file
         }
    
         Debug.Log("Boss save file created and state saved: " + BossDeafeated);
     }

    The game could work normally but the door didn’t trigger. I must stop the game and re opened to make the door is trigger. as you could see in the recording below Door stuck record

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

    The 2nd way of saving is the correct one. The reason your door is not firing has nothing to do with the saving, I suspect.

    It is because this line is not firing after your boss dies.

    void DestroyAfterDeath()
    {
           SpawnBoss.Instance.isnotTrigger();
           CorruptedMonk.Instance.DestroyAfterDeath();
           GameManager.Instance.bossDeafeated = true;
           SaveData.Instance.saveBoss();
           Debug.Log("Save boss data");
           SaveData.Instance.savePlayerData();
    }

    You need to figure out why. Perhaps the SpawnBoss object is disabled when the boss dies?

    #16728
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    Sorry for replying late, I got a long day. So about this, I think the problem is just like as you said but doesn’t know why it happend. The code still works after defeating the boss and turn off and turn on the game again. So it mean it work but not at the time at the boss was be beaten. And I really have no idea what cause of this trouble

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

    A simple way to check for this is add the following lines to both your functions:

    void DestroyAfterDeath()
    {
        print("DestroyAfterDeath() running");
        SpawnBoss.Instance.isnotTrigger();
        CorruptedMonk.Instance.DestroyAfterDeath();
        GameManager.Instance.bossDeafeated = true;
        SaveData.Instance.saveBoss();
        Debug.Log("Save boss data");
        SaveData.Instance.savePlayerData();
    }
    public void isnotTrigger()
    {
        print("isnotTrigger() running");
        col.isTrigger = true;
    }

    After that, kill the boss and see whether both the messages we coded to print is firing. The reason your trigger is not starting is because isnotTrigger() isn’t working, but we need to find out where is your code failing.

    If both messages don’t appear, then this means that DestroyAfterDeath() isn’t firing — in that case, we will have to figure out which function(s) are supposed to call DestroyAfterDeath(), and why it isn’t firing.

    Does that make sense?

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

Go to Login Page →


Advertisement below: