Forum begins after the advertisement:


[Part 8] Article Changes, Common Issues & Bugfixes

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [Part 8] Article Changes, Common Issues & Bugfixes

Viewing 1 post (of 1 total)
  • Author
    Posts
  • #14653
    Joseph Tang
    Moderator

    This is an update post to summarize the changes made to the content of the Metroidvania series.
    Additionally, this post is to compile and go through some common issues that viewers and readers may face when following the Metroidvania series, as well as some solutions that may fix or solve the issues.

    Some solutions given will also be under the assumptions that the code for the mechanic is 1:1 for the relevant part.

    In this post, we will go through
    [Part 8]

      *Article Changes*

    • 1. Mana orbs don’t gain mana while mana is container is halved
    • 2. Non-functional WallJump/WallJump flips my player permanently
    • 3. Collectibles do not disappear when picking them up until after the pickup canvas has disappeared
    • 4. MaxHealth is not saved and does not appear correctly on start


    [Part 8] Wall Jump, Unlocking Abilities & Spells, Mask & Soul Vessel Shards and Inventory

    Article Changes

    1. Mana orbs don’t gain mana while mana is container is halved

        void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool, Vector2 _recoilDir, float _recoilStrength)
        {
            ...
            for(int i = 0; i < objectsToHit.Length; i++)
            {
                if (objectsToHit[i].GetComponent() != null)
                {
                    objectsToHit[i].GetComponent().EnemyHit
                        (damage,  _recoilDir, _recoilStrength);
    
                    if (objectsToHit[i].CompareTag("Enemy"))
                    {
                        if (Mana < 1) if(!halfMana && Mana < 1 || (halfMana && Mana < 0.5))
                        {
                            Mana += manaGain;
                        }
                        else
                        {
                            manaOrbsHandler.UpdateMana(manaGain * 3);
                        }
                    }
                }
            }
        }

    2. Non-functional WallJump/WallJump flips my player permanently

    • Remove the if statement in the PlayerController.cs WallJump() method.
    • Then add a line to reverse the playerstate bool of lookingRight, and then set the eulerAngles to Vector2(transform.eulerAngles.x, 180).
    • Finally, reset the eulerAngles in StopWallJumping().
        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 * wallJumpingPower.x, wallJumpingPower.y);
    
                dashed = false;
                airJumpCounter = 0;
    
                pState.lookingRight = !pState.lookingRight;
                transform.eulerAngles = new Vector2(transform.eulerAngles.x, 180);
    
                if(pState.lookingRight && transform.eulerAngles.y == 0) || (!pState.lookingRight && transform.eulerAngles.y != 0)
                {
                    pState.lookingRight = !pstate.lookingRight;
                    int _yRotation = pState.lookingRight ? 0 : 180;
    
                    transform.eulerAngles = new Vector2(transform.eulerAngles.x, _yRotation);
                }
    
                Invoke(nameof(StopWallJumping), wallJumpingDuration);
            }
        }
        void StopWallJumping()
        {
            isWallJumping = false;
            transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0);
        }

    Explanation of issue: Euler Angles
    When using “int _yRotation = pState.lookingRight ? 0 : 180;” in “WallJump()”, it sets the “eulerAngles.y” to either [0] or [180], depending on the Player’s “lookingRight” bool. The idea is to have the player bounce off the wall and face away from the wall when they do so. The issue with this code is that “eulerAngles” aren’t affected by normal forms of rotation, like movement keys in game or the Transform Rotation in the inspector, as can be seen in here:

    Rather, they have to be reset by code back to a [0] value / normal to have your Player matching up correctly with your intended actions. Thus, we reset the “eulerAngles.y” to 0 when we write the “StopWallJumping()” code, where the player bounces away from the wall and stays that way for the set “wallJumpingDuration” before being free to move.
    Understanding how Euler Angles work, we can remove the differentiated “pState.lookingRight” code from “WallJump()” as they do not affect the “eulerAngles” as intended. Thus, allowing both directions of wall jumping to bounce off the wall.


    3. Collectibles do not disappear when picking them up until after the pickup canvas has disappeared

    • Add “gameObject.GetComponent().enabled = false;” into all Collectibles’ scripts with ShowUI()
        IEnumerator ShowUI()
        {
            GameObject _particles = Instantiate(particles, transform.position, Quaternion.identity);
            Destroy(_particles, 0.5f);
            yield return new WaitForSeconds(0.5f);
            gameObject.GetComponent().enabled = false;
    
            canvasUI.SetActive(true);
    
            yield return new WaitForSeconds(4f);
            PlayerController.Instance.unlockedWallJump = true;
            canvasUI.SetActive(false);
            Destroy(gameObject);
        }

    4. MaxHealth is not saved and does not appear correctly on start

    [This issue is caused by a missing piece of code]

    SaveData.cs

    [System.Serializable]
    public struct SaveData
    {
        public static SaveData Instance;
    
        //player stuff
        public int playerHealth;
        public int playerMaxHealth;
        public int playerHeartShards;
        public float playerMana;
        public bool playerHalfMana;
        public Vector2 playerPosition;
        public string lastScene;
    
        public bool playerUnlockedWallJump, playerUnlockedDash, playerUnlockedVarJump;
        public bool playerUnlockedSideCast, playerUnlockedUpCast, playerUnlockedDownCast;
    
        #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);
    
                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()
        {
            if(File.Exists(Application.persistentDataPath + "/save.player.data"))
            {
                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();
    
                    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.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 doesnt 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
    }

    PlayerController.cs

        // Start is called before the first frame update
        void Start()
        {
            pState = GetComponent();
    
            rb = GetComponent();
            sr = GetComponent();
    
            anim = GetComponent();
    
            SaveData.Instance.LoadPlayerData();
            if (halfMana)
            {
                UIManager.Instance.SwitchMana(UIManager.ManaState.HalfMana);
            }
            else
            {
                UIManager.Instance.SwitchMana(UIManager.ManaState.FullMana);
            }
    
            onHealthChangedCallback.Invoke();
    
            gravity = rb.gravityScale;
    
            if (Health == 0)
            {
                pState.alive = false;
                GameManager.Instance.RespawnPlayer();
            }
    
            Mana = mana;
            manaStorage.fillAmount = Mana;
    
            Health = maxHealth;
        }


    That will be all for Part 8.
    Hopefully this can help you on any issues you may have. However, if you find that your issues weren’t addressed or is a unique circumstance, you can submit a forum post to go into detail on your problem for further assistance.

Viewing 1 post (of 1 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: