Forum begins after the advertisement:


Player do not take damage & make time very slow when jump off

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity Player do not take damage & make time very slow when jump off

Viewing 8 posts - 1 through 8 (of 8 total)
  • Author
    Posts
  • #16198
    pepecry
    Participant
    Helpful?
    Up
    0
    ::

    Hello guy. So i’m on start of part 7. I was making death function but my player doesn’t take dmg even though the take damage method I just added an if statement like in the video. Otherwise everything else i just copy and paste. When i got hit, the time is freeze which mean i got take dmg but for some reason the health is not minus like in the video
    Record dmg test
    Record of test dmg function and errors
    And as the below video, you guyys could see that the game will be very slowly if I got hit by the moster and try to jump up. This is not happend before the death function is added. For the code, here is my player controller code

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using Unity.Mathematics;
    using UnityEditor.Tilemaps;
    using UnityEngine;
    using UnityEngine.UI;
    
    [RequireComponent(typeof(Rigidbody2D))]
    public class Move : MonoBehaviour
    {
        //animation and physic
        [Header("physic of player")]
        private Rigidbody2D rbd2;
        [SerializeField] public float speed = 2.0f;
        private float xAxis, yAxis;
        [SerializeField] private float jumpForce = 10;
        private int dbJumpCounter;
        [SerializeField] private int maxdbJump = 1;
        [Space(5)]
    
        [Header("Groundcheck setting")]
        [SerializeField] private Transform groundcheck;
        [SerializeField] private float groundchecky = 0.2f;
        [SerializeField] private float groundcheckx = 0.5f;
        [SerializeField] private LayerMask isground;
        Animator animator;
        [HideInInspector] public PlayerStateList playerStateList;
        [Space(5)]
    
        [Header("Coyotetime setting")]
        [SerializeField] private float coyoteTime = 0.1f;
        private float coyoteTimeCounter = 0;
        [Space(5)]
    
        [Header("Attacking setting")]
        [SerializeField] private float timeBetweenattck;
        [SerializeField] Transform sideAttackTransform, upAttackTransform, downAttackTransform;
        [SerializeField] Vector2 sideAttackArea, upAttackArea, downAttackArea;
        [SerializeField] LayerMask attackableLayer;
        [SerializeField] float playerDmg;
        private float timeSinceattk;
        bool attk = false;
        [Space(5)]
    
        [Header("Health Setting")]
        [SerializeField] public int health;
        [SerializeField] public int MaxHealth;
        [SerializeField] GameObject bloodSpurt;
        [SerializeField] float hitFlashspeed;
        public delegate void OnHealthChangedDelegate();
        [HideInInspector] public OnHealthChangedDelegate onHealthChangedCallback;
        float healTimer;
        [SerializeField] float timetoHeal;
        [Space(5)]
    
        [Header("Dash setting")]
        [SerializeField] private float dashSpeed;
        [SerializeField] private float dashTime;
        [SerializeField] private float dashCD;
        private bool canDash = true;
        private bool dashed;
        [Space(5)]
    
        [Header("Recoil")]
        [SerializeField] int recoilXSteps = 5;
        [SerializeField] int recoilYSteps = 5;
        [SerializeField] float recoilXSpeed = 100;
        [SerializeField] float recoilYSpeed = 100;
        int stepXrecoiled, stepYrecoiled;
        [Space(5)]
    
        [Header("Mana Setting")]
        [SerializeField] float mana;
        [SerializeField] float manaDrain;
        [SerializeField] float manaGain;
        [SerializeField] UnityEngine.UI.Image manaStorage;
        [Space(5)]
    
        [Header("Spellcasting")]
        [SerializeField] float manaSpellcost = 0.3f;
        [SerializeField] float timeBetweencast = 0.5f;
         float timeSincecast;
        float castOrhealTimer;
        [SerializeField] float spellDmg; //dark rising and skyfall only
        [SerializeField] float skyfallForce; // force for skyfall
        [SerializeField] GameObject sideSpellDarkBall;
        [SerializeField] GameObject upSpellDarkRising;
        [SerializeField] GameObject downSpellSkyFall;
        [Space(5)]
    
        bool restoreTime;
        float restoreTimeSpeed;
        private SpriteRenderer spriteRenderer;
        public static Move Instance;
        private float gravity;
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
        }
        // Start is called before the first frame update
        private void Start()
        {
            //player game object
            playerStateList = GetComponent<PlayerStateList>();
            rbd2 = GetComponent<Rigidbody2D>();
            animator = GetComponent<Animator>();
            spriteRenderer = GetComponent<SpriteRenderer>();
            gravity = rbd2.gravityScale;
            Mana = mana;
            manaStorage.fillAmount = Mana;
            Health = MaxHealth;
        }
    
        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
    
        private void Update()
        {
            if (playerStateList.incutscene) return;
            if (playerStateList.alive)
            {
                getInput();
            }
            UpdateJump();
            RestoreTimeScale();
            if (playerStateList.dashing || playerStateList.healing) return;
            FlashWhenInvi();
            if (playerStateList.alive)
            {
                Moving();
                Heal();
                CastSpell();
                Flip();
                Jump();
                StartDash();
                Attack();
            }
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.GetComponent<Enemy>() != null && playerStateList.castspell)
            {
                _other.GetComponent<Enemy>().EnemyHit(spellDmg, (_other.transform.position - _other.transform.position).normalized, -recoilYSpeed);
            }
        }
    
        private void FixedUpdate()
        {
            if (playerStateList.incutscene) return;
            if (playerStateList.dashing) return;
            Recoil();
        }
    
        void getInput()
        {
            xAxis = Input.GetAxisRaw("Horizontal");
            yAxis = Input.GetAxisRaw("Vertical");
            attk = Input.GetButtonDown("Attack");
    
            if (Input.GetButton("Cast/Heal"))
            {
                castOrhealTimer += Time.deltaTime;
            }
            else
            {
                castOrhealTimer = 0;
            }
        }
        //player Attack
        void Attack()
        {
            timeSinceattk += Time.deltaTime;
            if (attk && timeSinceattk >= timeBetweenattck)
            {
                timeSinceattk = 0;
                animator.SetTrigger("Attacking");
    
                if (yAxis == 0 || yAxis < 0 && isonGround())
                {
                    int _recoilLeftorRight = playerStateList.lookingRight ? 1 : -1;
                    Hit(sideAttackTransform, sideAttackArea, ref playerStateList.recoilingX, recoilXSpeed, Vector2.right * _recoilLeftorRight);
                }
                else if (yAxis > 0)
                {
                    Hit(upAttackTransform, upAttackArea, ref playerStateList.recoilingY, recoilYSpeed, Vector2.up);
                }
                else if (yAxis < 0 && !isonGround())
                {
                    Hit(downAttackTransform, downAttackArea, ref playerStateList.recoilingY, recoilYSpeed, Vector2.down);
                }
            }
        }
    
        //Using for the character recoil
        void Recoil()
        {
            if (playerStateList.recoilingX)
            {
                if (playerStateList.lookingRight)
                {
                    rbd2.velocity = new Vector2(-recoilXSpeed, 0);
                }
                else
                {
                    rbd2.velocity = new Vector2(recoilXSpeed, 0);
                }
            }
    
            if (playerStateList.recoilingY)
            {
                rbd2.gravityScale = 0;
                if (yAxis < 0)
                {
    
                    rbd2.velocity = new Vector2(rbd2.velocity.x, recoilYSpeed);
                }
                else
                {
                    rbd2.velocity = new Vector2(rbd2.velocity.x, -recoilYSpeed);
                }
                dbJumpCounter = 0;
            }
            else
            {
                rbd2.gravityScale = gravity;
            }
            //Stop recoil X
            if (playerStateList.recoilingX && stepXrecoiled < recoilXSteps)
            {
                stepXrecoiled++;
            }
            else
            {
                StopRecoilX();
            }
            //Stop recoil Y
            if (playerStateList.recoilingY && stepYrecoiled < recoilYSteps)
            {
                stepYrecoiled++;
            }
            else
            {
                StopRecoilY();
            }
    
            // if hit ground stop recoil Y
            if (isonGround())
            {
                StopRecoilY();
            }
        }
    
        void StopRecoilX()
        {
            stepXrecoiled = 0;
            playerStateList.recoilingX = false;
        }
        void StopRecoilY()
        {
            stepYrecoiled = 0;
            playerStateList.recoilingY = false;
        }
        //player hit check
        void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool, float _recoilStrenght, Vector2 _recoilDir)
        {
            Collider2D[] objecttoHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer);
            if (objecttoHit.Length > 0)
            {
                _recoilBool = true;
            }
    
            for (int i = 0; i < objecttoHit.Length; i++)
            {
                if (objecttoHit[i].GetComponent<Enemy>() != null)
                {
                    objecttoHit[i].GetComponent<Enemy>().EnemyHit(playerDmg, _recoilDir, _recoilStrenght);
                    if (objecttoHit[i].CompareTag("Enemy"))
                    {
                        Mana += manaGain;
                    }
                }
            }
        }
        //change dir of the player
        void Flip()
        {
            if (xAxis < 0)
            {
                transform.localScale = new Vector2(-1, transform.localScale.y);
                playerStateList.lookingRight = false;
            }
            else if (xAxis > 0)
            {
                transform.localScale = new Vector2(1, transform.localScale.y);
                playerStateList.lookingRight = true;
            }
        }
        // start the dash
        void StartDash()
        {
            if (Input.GetButtonDown("Dash") && canDash && !dashed)
            {
                StartCoroutine(Dash());
                dashed = true;
            }
            if (isonGround())
            {
                dashed = false;
            }
        }
        IEnumerator Dash()
        {
            canDash = false;
            playerStateList.dashing = true;
            animator.SetTrigger("Dashing");
            rbd2.gravityScale = 0;
            int _dir = playerStateList.lookingRight ? 1 : -1;
            rbd2.velocity = new Vector2(_dir * dashSpeed, 0);
            yield return new WaitForSeconds(dashTime);
            rbd2.gravityScale = gravity;
            playerStateList.dashing = false;
            yield return new WaitForSeconds(dashCD);
            canDash = true;
        }
        //moving of player
        private void Moving()
        {
            //if(playerStateList.healing) rbd2.velocity = new Vector2(0,0);
            rbd2.velocity = new Vector2(speed * xAxis, rbd2.velocity.y);
            animator.SetBool("Walking", rbd2.velocity.x != 0 && isonGround());
        }
        //check if player is on the ground or not
        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)
                )
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        //player jump
        void Jump()
        {
    
            if (Input.GetButtonDown("Jump") && coyoteTimeCounter > 0)
            {
                rbd2.velocity = new Vector3(rbd2.velocity.x, jumpForce);
                /* playerStateList.Jumping = true;*/
            }
            else if (!isonGround() && dbJumpCounter < maxdbJump && Input.GetButtonDown("Jump"))
            {
                dbJumpCounter++;
                rbd2.velocity = new Vector3(rbd2.velocity.x, jumpForce);
            }
    
            animator.SetBool("Jumping", !isonGround());
        }
        void UpdateJump()
        {
            if (isonGround())
            {
                /* playerStateList.Jumping = false;*/
                coyoteTimeCounter = coyoteTime;
                dbJumpCounter = 0;
            }
            else
            {
                coyoteTimeCounter -= Time.deltaTime;
            }
        }
        //stop taking dmg when player get hit
        IEnumerator StopTakingDmg()
        {
            playerStateList.Invi = true;
            GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, Quaternion.identity);
            Destroy(_bloodSpurtParticles, 1f);
            animator.SetTrigger("TakeDmg");
            yield return new WaitForSeconds(1f);
            playerStateList.Invi = false;
        }
        //player health calculated
        public int Health
        {
            get { return health; }
            set
            {
                if (health != value)
                {
                    health = Mathf.Clamp(value, 0, MaxHealth);
    
                    if (onHealthChangedCallback != null)
                    {
                        onHealthChangedCallback.Invoke();
                    }
                }
            }
        }
        float Mana
        {
            get { return mana; }
            set
            {
                if (mana != value)
                {
                    mana = Mathf.Clamp(value, 0, 1);
                    manaStorage.fillAmount = Mana;
                }
            }
    
        }
        //take dmg function
        public void takeDmg(float _Dmg)
        {
            if (!playerStateList.alive)
            {
                Health -= Mathf.RoundToInt(_Dmg);
                if(Health <= 0)
                {
                    Health = 0;
                    StartCoroutine(Death());
                }
                else
                {
                    StartCoroutine(StopTakingDmg());
                }
          
            } 
        }
        //time delay for player when get hit
        public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay)
        {
            restoreTimeSpeed = _restoreSpeed;
            Time.timeScale = _newTimeScale;
            if (_delay > 0)
            {
                StopCoroutine(TimeStartAgain(_delay));
                StartCoroutine(TimeStartAgain(_delay));
            }
            else
            {
                restoreTime = true;
            }
        }
    
        void RestoreTimeScale()
        {
            if (restoreTime)
            {
                if (Time.timeScale < 1)
                {
                    Time.timeScale += Time.unscaledDeltaTime * restoreTimeSpeed;
                }
                else
                {
                    Time.timeScale = 1;
                    restoreTime = false;
                }
            }
        }
        IEnumerator TimeStartAgain(float _delay)
        {
            yield return new WaitForSecondsRealtime(_delay);
            restoreTime = true;
    
        }
    
        void FlashWhenInvi()
        {
            spriteRenderer.material.color = playerStateList.Invi ? Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashspeed, 0.5f)) : Color.white;
        }
        void Heal()
        {
            if (Input.GetButton("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing)
            {
                print("Healing: " + playerStateList.healing + ", Timer:" + healTimer + ", Mana:" + Mana);
                playerStateList.healing = true;
                animator.SetBool("Healing", true);
                healTimer += Time.deltaTime;
                if (healTimer >= timetoHeal)
                {
                    Health++;
                    healTimer = 0;
                }
    
                //using mana to heal
                Mana -= Time.deltaTime * manaDrain;
            }
            else
            {
               
                playerStateList.healing = false;
                animator.SetBool("Healing", false);
                healTimer = 0;
            }
        }
    
        void CastSpell()
        {
            if (Input.GetButtonUp("Cast/Heal") && castOrhealTimer <= 0.05f && timeSincecast >= timeBetweencast && Mana >= manaSpellcost)
    
            {
                playerStateList.castspell = true;
                timeSincecast = 0;
                StartCoroutine(CastCoroutine());
            }
            else 
            {
                timeSincecast += Time.deltaTime;
            }
    
            if (isonGround())
            {
                //cannot using skyfall if on ground
                downSpellSkyFall.SetActive(false);
            }
            //force the player to fall down if cast skyfall
            if (downSpellSkyFall.activeInHierarchy)
            {
                rbd2.velocity += skyfallForce * Vector2.down;
            }
        }
    
        IEnumerator CastCoroutine()
        {
            animator.SetBool("Casting", true);
            yield return new WaitForSeconds(0.12f);
    
            //sidecast/darkball
            if(yAxis == 0 || (yAxis < 0 && isonGround()))
            {
                GameObject _darkball = Instantiate(sideSpellDarkBall, sideAttackTransform.position, Quaternion.identity);
    
                //flip skill
                if (playerStateList.lookingRight)
                {
                    _darkball.transform.eulerAngles = Vector3.zero; // ban spell nhu bth
                }
                else 
                {
                    _darkball.transform.eulerAngles = new Vector2(_darkball.transform.eulerAngles.x, 100);
                }
    
                playerStateList.recoilingX = true;
            }
    
            //upcast/Dark Rising
            else if(yAxis > 0)
            {
                Instantiate(upSpellDarkRising, transform);
                rbd2.velocity = Vector2.zero;
            }
    
            //downcasr/ Sky Fall
            else if (yAxis < 0 && !isonGround())
            {
                downSpellSkyFall.SetActive(true);
            }
    
            Mana -= manaSpellcost;
            yield return new WaitForSeconds(0.35f);
            animator.SetBool("Casting", false);
            playerStateList.castspell = false;
        }
    
        public IEnumerator WalktonewScene(Vector2 _exitDir, float _delay)
        {
            if(_exitDir.y > 0)
            {
                rbd2.velocity = jumpForce * _exitDir;
            }
            if (_exitDir.x != 0) 
            {
                xAxis = _exitDir.x > 0 ? 1 : -1;
                Moving();
            }
            Flip();
            yield return new WaitForSeconds(_delay);
            playerStateList.incutscene = false;
        }
    
        IEnumerator Death()
        {
            playerStateList.alive = false;
            Time.timeScale = 1f;
            animator.SetTrigger("Death");
            yield return new WaitForSeconds(0.9f);
            StartCoroutine(UIManager.Instance.ActiveDeathScreen());
        }
    }
    

    Here is the UI Manager script

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class UIManager : MonoBehaviour
    {
        // Start is called before the first frame update
        public static UIManager Instance;
    
        [SerializeField] GameObject deathSceen;
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
        }
    
        public SceneFading sceneFading;
        public void Start()
        {
            sceneFading = GetComponentInChildren<SceneFading>();
        }
    
        public IEnumerator ActiveDeathScreen()
        {
            yield return new WaitForSeconds(0.8f);
            StartCoroutine(sceneFading.Fade(SceneFading.fadeDirection.In));
            yield return new WaitForSeconds(0.9f);
            deathSceen.SetActive(true);
        }
    }
    

    The only path I change in the enemy script is here

    protected void OnCollisionStay2D(Collision2D _other)
    {
        if (_other.gameObject.CompareTag("Player") && !Move.Instance.playerStateList.Invi && !Move.Instance.playerStateList.Invi && health > 0)
        {
            Attack();
            if (Move.Instance.playerStateList.alive)
            {
                Move.Instance.HitStopTime(0, 2, 0.5f);
            }
    
        }
    }
    

    Tks you guys.

    #16201
    Chloe Lim
    Moderator
    Helpful?
    Up
    0
    ::

    Well firstly, your takedamage function has the wrong if statement it should be
    if (playerStateList.alive) but the one there now is if (!playerStateList.alive)
    Make this fix first, then see what still does not work

    #16202
    pepecry
    Participant
    Helpful?
    Up
    0
    ::

    Ok it work but i found 2 other problem.
    The first error is happend with the input (again) that when i change the update to match with the video liek this

     private void Update()
     {
         if (playerStateList.incutscene) return;
         if (playerStateList.alive)
         {
             getInput();
         }
         UpdateJump();
         RestoreTimeScale();
         if (playerStateList.dashing || playerStateList.healing) return;
         FlashWhenInvi();
         if (playerStateList.alive)
         {
             Moving();
             Heal();
             CastSpell();
             Flip();
             Jump();
             StartDash();
             Attack();
         }
     }

    When i hold to heal, it stuck at the heal status but the heal doesn’t grow up. And you can’t moving or doing anything. Just stuck at healing forever without any health get. The Mana didn’t minus to heal the player.
    The second problem with the player is when player death but the enemy is to close, the player got push very far and there is 2 possibilities will happen. The first one is player got hit very far and the second is slowly go off. It’s kinda hard to explain but you can see in the record below. And the death scene doesn’t work perfectly. Sometime it just a black sceen and sometime it a death scene. Both of that is in the record i show you below
    Record about the death scene
    Death scene error record
    So how can i make my player stop at where it dead and make the death scene work perfectly everytime. tks you for helping me.

    #16203
    Chloe Lim
    Moderator
    Helpful?
    Up
    0
    ::

    Heal() and cast() needs to be above the order of if(pState.healing) return , it should generally be like this

        void Update()
        {
            if (playerStateList.incutscene) return;
    
            if(playerStateList.alive)
            {
                GetInputs();
            }
            UpdateJump();
            RestoreTimeScale();
            if (playerStateList.dashing) return;
            FlashWhileInvi();
            if (!playerStateList.alive) return;
            Move();
            Heal();
            CastSpell();
            if (playerStateList.healing) return;
            Flip();
            Jump();
            StartDash();
            Attack();
        }
    #16208
    pepecry
    Participant
    Helpful?
    Up
    0
    ::

    Tks. The heal work again but do you have any idea to fix the death scene? It got kinda random. Sometime it like the record I sent you before. Sometime it could look like this
    Death scene
    And any idea to fix the player so he would stand still but not being floating when death?

    #16210
    Chloe Lim
    Moderator
    Helpful?
    Up
    0
    ::

    Try to add rb.constraints = RigidbodyContraints2D.FreezePosition to your player death function
    for the gameover screen, ensure that the gameove text and button is below in the hiearchy of the scene fader, like this

    #16214
    pepecry
    Participant
    Helpful?
    Up
    0
    ::

    Ok so it work to fix both the problem but then I figure out that the respawn just don’t work. I go to the altar and spam the f key alot but when i died and click respawn button. It just spawn me in the middle of the room and my character freeze
    Interact project setting
    Respawn code

    public void respawnPlayer()
    {
        if(alter != null)
        {
            if (alter.Interacted)
            {
                respawnpoint = alter.transform.position;
            }
            else
            {
                respawnpoint = platformRespawnpoint;
            }
        }
        else
        {
            respawnpoint = platformRespawnpoint;
        }
        Move.Instance.transform.position = respawnpoint;
        StartCoroutine(UIManager.Instance.DeactiveDeathScreen());
        Move.Instance.Respawn();
    }

    Alter code

    private void OnTriggerStay2D(Collider2D _collision)
    {
        if (_collision.CompareTag("Player") && Input.GetButtonDown("Interact"))
        {
            Interacted = true;
        }
    }

    after respawn
    Respawn freeze video
    As you can see in the video. After get respawn i’m stuck and can’t move anywhere. Abd also sometime I respawn at the alter and sometime even if I interact with the alter. It still repsawn me in middle of the room like the img. but we could just skip the respawn point for now. How should I fix the free after respawn? thank you

    #16215
    Chloe Lim
    Moderator
    Helpful?
    Up
    0
    ::

    Ok, i forgot to add that since the rb constraints are frozen when the player dies, you definitely need to remove the constraints when the player respawns

                rb.constraints = RigidbodyConstraints2D.None;
                rb.constraints = RigidbodyConstraints2D.FreezeRotation;

    put this code in the respawnplayerfunction
    Also for the altar, put the code in the Update() function instead of the OnTriggerStay2D() function, i think it might help

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

Go to Login Page →


Advertisement below: