Forum begins after the advertisement:


[Part 5] Healing and Cast problems

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [Part 5] Healing and Cast problems

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #15864
    Bachok
    Participant
    Helpful?
    Up
    0
    ::

    Hello everyone! I have a problem again, but it’s not so significant anymore, in short, the first is that my enemy doesn’t take damage from spells at all, and the second is that when I press the button to heal, my character freezes with the healing animation and does nothing else. I also want to note that there are no errors either in Unit or in the console with the code itself. Sorry to bother you again, I hope you can help me with something. Thank you in advance.

    Video: https://i.imgur.com/jHXB0rM.mp4

    Code of my PlayerController:

    using System.Collections;
    using System.Collections.Generic;
    using System.Net.Sockets;
    using UnityEngine;
    using UnityEngine.UIElements;
    using UnityEngine.UI;
    
    public class PlayerController : MonoBehaviour
    {
        [Header("Horizontal Movement Settings:")]
        [SerializeField] private float walkSpeed = 1; //sets the players movement speed on the ground
        [Space(5)]
    
        [Header("Vertical Movement Settings")]
        [SerializeField] private float jumpForce = 45f; //sets how hight the player can jump
    
        private int jumpBufferCounter = 0; //stores the jump button input
        [SerializeField] private int jumpBufferFrames; //sets the max amount of frames the jump buffer input is stored
    
        private float coyoteTimeCounter = 0; //stores the Grounded() bool
        [SerializeField] private float coyoteTime; ////sets the max amount of frames the Grounded() bool is stored
    
        private int airJumpCounter = 0; //keeps track of how many times the player has jumped in the air
        [SerializeField] private int maxAirJumps; //the max no. of air jumps
    
        private float gravity; //stores the gravity scale at start
        [Space(5)]
    
        [Header("Ground Check Settings:")]
        [SerializeField] private Transform groundCheckPoint; //point at which ground check happens
        [SerializeField] private float groundCheckY = 0.2f; //how far down from ground chekc point is Grounded() checked
        [SerializeField] private float groundCheckX = 0.5f; //how far horizontally from ground chekc point to the edge of the player is
        [SerializeField] private LayerMask whatIsGround; //sets the ground layer
        [Space(5)]
    
        [Header("Dash Settings")]
        [SerializeField] private float dashSpeed; //speed of the dash
        [SerializeField] private float dashTime; //amount of time spent dashing
        [SerializeField] private float dashCooldown; //amount of time between dashes
        [SerializeField] GameObject dashEffect;
        private bool canDash = true, dashed;
        [Space(5)]
    
        [Header("Attack Settings:")]
        [SerializeField] private Transform SideAttackTransform; //the middle of the side attack area
        [SerializeField] private Vector2 SideAttackArea; //how large the area of side attack is
    
        [SerializeField] private Transform UpAttackTransform; //the middle of the up attack area
        [SerializeField] private Vector2 UpAttackArea; //how large the area of side attack is
    
        [SerializeField] private Transform DownAttackTransform; //the middle of the down attack area
        [SerializeField] private Vector2 DownAttackArea; //how large the area of down attack is
    
        [SerializeField] private LayerMask attackableLayer; //the layer the player can attack and recoil off of
    
        private float timeBetweenAttack, timeSinceAttck;
    
        [SerializeField] private float damage; //the damage the player does to an enemy
    
        [SerializeField] private GameObject slashEffect; //the effect of the slashs
    
        bool restoreTime;
        float restoreTimeSpeed;
        [Space(5)]
    
        [Header("Recoil Settings:")]
        [SerializeField] private int recoilXSteps = 5; //how many FixedUpdates() the player recoils horizontally for
        [SerializeField] private int recoilYSteps = 5; //how many FixedUpdates() the player recoils vertically for
    
        [SerializeField] private float recoilXSpeed = 100; //the speed of horizontal recoil
        [SerializeField] private float recoilYSpeed = 100; //the speed of vertical recoil
    
        private int stepsXRecoiled, stepsYRecoiled; //the no. of steps recoiled horizontally and verticall
        [Space(5)]
    
        [Header("Health Settings")]
        public int health;
        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("Mana Settings")]
        [SerializeField] UnityEngine.UI.Image manaStorage;
    
        [SerializeField] float mana;
        [SerializeField] float manaDrainSpeed;
        [SerializeField] float manaGain;
        [Space(5)]
    
        [Header("Spell Settings")]
        //spell stats
        [SerializeField] float manaSpellCost = 0.3f;
        [SerializeField] float timeBetweenCast = 0.5f;
        float timeSinceCast;
        [SerializeField] float spellDamage; //upspellexplosion and downspellfireball
        [SerializeField] float downSpellForce; // desolate dive only
        //spell cast objects
        [SerializeField] GameObject sideSpellFireball;
        [SerializeField] GameObject upSpellExplosion;
        [SerializeField] GameObject downSpellFireball;
        [Space(5)]
    
        [HideInInspector] public PlayerStateList pState;
        private Animator anim;
        private Rigidbody2D rb;
        private SpriteRenderer sr;
    
        //Input Variables
        private float xAxis, yAxis;
        private bool attack = false;
    
        public static PlayerController Instance;
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
        }
    
        // Start is called before the first frame update
        void Start()
        {
            pState = GetComponent<PlayerStateList>();
    
            rb = GetComponent<Rigidbody2D>();
            sr = GetComponent<SpriteRenderer>();
    
            anim = GetComponent<Animator>();
    
            gravity = rb.gravityScale;
    
            Mana = mana;
            manaStorage.fillAmount = Mana;
        }
    
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireCube(SideAttackTransform.position, SideAttackArea);
            Gizmos.DrawWireCube(UpAttackTransform.position, UpAttackArea);
            Gizmos.DrawWireCube(DownAttackTransform.position, DownAttackArea);
        }
    
        // Update is called once per frame
        void Update()
        {
            if (pState.cutscene) return;
    
            GetInputs();
            UpdateJumpVariables();
    
            if (pState.dashing || pState.healing) return;
            Flip();
            Move();
            Jump();
            StartDash();
            Attack();
            RestoreTimeScale();
            FlashWhileInvincible();
            Heal();
            CastSpell();
        }
        private void OnTriggerEnter2D(Collider2D _other) //for up and down cast spell
        {
            if (_other.GetComponent<Enemy>() != null && pState.casting)
            {
                _other.GetComponent<Enemy>().EnemyHit(spellDamage, (_other.transform.position - transform.position).normalized, -recoilYSpeed);
            }
        }
    
        private void FixedUpdate()
        {
            if (pState.cutscene) return;
            if (pState.dashing) return;
            Recoil();
        }
    
        void GetInputs()
        {
            xAxis = Input.GetAxisRaw("Horizontal");
            yAxis = Input.GetAxisRaw("Vertical");
            attack = Input.GetButtonDown("Attack");
        }
    
        void Flip()
        {
            if (xAxis < 0)
            {
                transform.localScale = new Vector2(-3, transform.localScale.y);
                pState.lookingRight = false;
            }
            else if (xAxis > 0)
            {
                transform.localScale = new Vector2(3, 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("Walking", rb.velocity.x != 0 && Grounded());
        }
    
        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("Dashing");
            rb.gravityScale = 0;
            int _dir = pState.lookingRight ? 1 : -1;
            rb.velocity = new Vector2(_dir * dashSpeed, 0);
            if (Grounded()) Instantiate(dashEffect, transform);
            if (!Grounded()) Instantiate(dashEffect, transform);
            yield return new WaitForSecondsRealtime(dashTime);
            rb.gravityScale = gravity;
            pState.dashing = false;
            yield return new WaitForSecondsRealtime(dashCooldown);
            canDash = true;
        }
    
       
        public IEnumerator WalkIntoNewScene(Vector2 _exitDir, float _delay)
        {
            if(_exitDir.y > 0)
            {
                rb.velocity = jumpForce * _exitDir;
            }
            if (_exitDir.x != 0)
            {
                xAxis = _exitDir.x > 0 ? 1: -1;
                Move();
            }
            Flip();
            yield return new WaitForSecondsRealtime(_delay);
            pState.cutscene = false;
        }
        
    
        void Attack()
        {
            timeSinceAttck += Time.deltaTime;
            if (attack && timeSinceAttck >= timeBetweenAttack)
            {
                timeSinceAttck = 0;
                anim.SetTrigger("Attacking");
    
                if (yAxis == 0 || yAxis < 0 && Grounded())
                {
                    Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX, recoilXSpeed);
                    Instantiate(slashEffect, SideAttackTransform);
                }
                else if (yAxis > 0)
                {
                    Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, recoilYSpeed);
                    SlashEffectAtAngle(slashEffect, 80, UpAttackTransform);
                }
                else if (yAxis < 0 && !Grounded())
                {
                    Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, recoilYSpeed);
                    SlashEffectAtAngle(slashEffect, -90, DownAttackTransform);
                }
            }
    
        }
        void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilDir, float _recoilStrength)
        {
            Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer);
    
            if (objectsToHit.Length > 0)
            {
                _recoilDir = true;
            }
            for (int i = 0; i < objectsToHit.Length; i++)
            {
                if (objectsToHit[i].GetComponent<Enemy>() != null)
                {
                    objectsToHit[i].GetComponent<Enemy>().EnemyHit
                        (damage, (transform.position - objectsToHit[i].transform.position).normalized, _recoilStrength);
                        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 / 3, transform.localScale.y / 3);
        }
        void Recoil()
        {
            if (pState.recoilingX)
            {
                if (pState.lookingRight)
                {
                    rb.velocity = new Vector2(-recoilXSpeed, 0);
                }
                else
                {
                    rb.velocity = new Vector2(recoilXSpeed, 0);
                }
            }
    
            if (pState.recoilingY)
            {
                rb.gravityScale = 0;
                if (yAxis < 0)
                {
                    rb.velocity = new Vector2(rb.velocity.x, recoilYSpeed);
                }
                else
                {
                    rb.velocity = new Vector2(rb.velocity.x, -recoilYSpeed);
                }
                airJumpCounter = 0;
            }
            else
            {
                rb.gravityScale = gravity;
            }
    
            //stop recoil
            if (pState.recoilingX && stepsXRecoiled < recoilXSteps)
            {
                stepsXRecoiled++;
            }
            else
            {
                StopRecoilX();
            }
            if (pState.recoilingY && stepsYRecoiled < recoilYSteps)
            {
                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)
        {
            Health -= Mathf.RoundToInt(_damage);
            StartCoroutine(StopTakingDamage());
        }
        IEnumerator StopTakingDamage()
        {
            pState.invincible = true;
            GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, Quaternion.identity);
            Destroy(_bloodSpurtParticles, 1.5f);
            anim.SetTrigger("TakeDamage");
            yield return new WaitForSecondsRealtime(1f);
            pState.invincible = false;
        }
        void FlashWhileInvincible()
        {
            sr.material.color = pState.invincible ? Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashSpeed, 1.0f)) : Color.white;
        }
        void RestoreTimeScale()
        {
            if (restoreTime)
            {
                if (Time.timeScale < 1)
                {
                    Time.timeScale += Time.unscaledDeltaTime * restoreTimeSpeed;
    
                }
                else
                {
                    Time.timeScale = 1;
                    restoreTime = false;
                }
            }
        }
        public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay)
        {
            restoreTimeSpeed = _restoreSpeed;
            if (_delay > 0)
            {
                StopCoroutine(StartTimeAgain(_delay));
                StartCoroutine(StartTimeAgain(_delay));
            }
            else
            {
                restoreTime = true;
            }
            Time.timeScale = _newTimeScale;
        }
        IEnumerator StartTimeAgain(float _delay)
        {
            yield return new WaitForSecondsRealtime(_delay);
            restoreTime = true;
        }
        public int Health
        {
            get { return health; }
            set
            {
                if (health != value)
                {
                    health = Mathf.Clamp(value, 0, maxHealth);
    
                    if (onHealthChangedCallback != null)
                    {
                        onHealthChangedCallback.Invoke();
                    }
                }
            }
        }
        void Heal()
        {
            if (Input.GetButton("Healing") && Health < maxHealth && Mana > 0 && Grounded() && !pState.dashing)
            {
                pState.healing = true;
                anim.SetBool("Healing", true);
    
                //healing
                healTimer += Time.deltaTime;
                if (healTimer >= timeToHeal)
                {
                    Health++;
                    healTimer = 0;
                }
    
                //drain mana
                Mana -= Time.deltaTime * manaDrainSpeed;
            }
            else
            {
                pState.healing = false;
                anim.SetBool("Healing", false);
                healTimer = 0;
            }
        }
        float Mana
        {
            get { return mana; }
            set
            {
                //if mana stats change
                if (mana != value)
                {
                    mana = Mathf.Clamp(value, 0, 1);
                    manaStorage.fillAmount = Mana;
                }
            }
        }
    
        void CastSpell()
        {
            if (Input.GetButtonDown("CastSpell") && timeSinceCast >= timeBetweenCast && Mana >= manaSpellCost)
            {
                pState.casting = true;
                timeSinceCast = 0;
                StartCoroutine(CastCoroutine());
            }
            else
            {
                timeSinceCast += Time.deltaTime;
            }
    
            if (Grounded())
            {
                //disable downspell if on the ground
                downSpellFireball.SetActive(false);
            }
            //if down spell is active, force player down until grounded
            if (downSpellFireball.activeInHierarchy)
            {
                rb.velocity += downSpellForce * Vector2.down;
            }
        }
        IEnumerator CastCoroutine()
        {
            anim.SetBool("Casting", true);
            yield return new WaitForSecondsRealtime(0.15f);
    
            //side cast
            if (yAxis == 0 || (yAxis < 0 && Grounded()))
            {
                GameObject _fireBall = Instantiate(sideSpellFireball, SideAttackTransform.position, Quaternion.identity);
    
                //flip fireball
                if (pState.lookingRight)
                {
                    _fireBall.transform.eulerAngles = Vector3.zero; // if facing right, fireball continues as per normal
                }
                else
                {
                    _fireBall.transform.eulerAngles = new Vector2(_fireBall.transform.eulerAngles.x, 180);
                    //if not facing right, rotate the fireball 180 deg
                }
                pState.recoilingX = true;
            }
    
            //up cast
            else if (yAxis > 0)
            {
                Instantiate(upSpellExplosion, transform);
                rb.velocity = Vector2.zero;
            }
    
            //down cast
            else if (yAxis < 0 && !Grounded())
            {
                downSpellFireball.SetActive(true);
            }
    
            Mana -= manaSpellCost;
            yield return new WaitForSecondsRealtime(0.35f);
            anim.SetBool("Casting", 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"))
            {
                pState.jumping = true;
    
                airJumpCounter++;
    
                rb.velocity = new Vector3(rb.velocity.x, jumpForce);
            }
    
            if (Input.GetButtonUp("Jump") && rb.velocity.y > 3)
            {
                pState.jumping = false;
    
                rb.velocity = new Vector2(rb.velocity.x, 0);
            }
    
            anim.SetBool("Jumping", !Grounded());
        }
    
        void UpdateJumpVariables()
        {
            if (Grounded())
            {
                pState.jumping = false;
                coyoteTimeCounter = coyoteTime;
                airJumpCounter = 0;
            }
            else
            {
                coyoteTimeCounter -= Time.deltaTime;
            }
    
            if (Input.GetButtonDown("Jump"))
            {
                jumpBufferCounter = jumpBufferFrames;
            }
            else
            {
                jumpBufferCounter--;
            }
        }
    }

    Code of PlayerStateList:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerStateList : MonoBehaviour
    {
        public bool jumping = false;
        public bool dashing = false;
        public bool recoilingX, recoilingY;
        public bool lookingRight;
        public bool invincible;
        public bool healing;
        public bool casting;
        public bool cutscene = false;
    }
    

    UIManager:

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class UIManager : MonoBehaviour
    {
        public SceneFader sceneFader;
    
        public static UIManager Instance;
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
    
            sceneFader = GetComponentInChildren<SceneFader>();
        }
    }

    HeartController:

    using UnityEngine;
    using UnityEngine.UI;
    
    public class HeartController : MonoBehaviour
    {
        private GameObject[] heartContainers;
        private Image[] heartFills;
        public Transform heartsParent;
        public GameObject heartContainerPrefab;
        // Start is called before the first frame update
        void Start()
        {
            heartContainers = new GameObject[PlayerController.Instance.maxHealth];
            heartFills = new Image[PlayerController.Instance.maxHealth];
    
            PlayerController.Instance.onHealthChangedCallback += UpdateHeartsHUD;
            InstantiateHeartContainers();
            UpdateHeartsHUD();
        }
    
        // Update is called once per frame
        void Update()
        {
    
        }
        void SetHeartContainers()
        {
            for (int i = 0; i < heartContainers.Length; i++)
            {
                if (i < PlayerController.Instance.maxHealth)
                {
                    heartContainers[i].SetActive(true);
                }
                else
                {
                    heartContainers[i].SetActive(false);
                }
            }
        }
        void SetFilledHearts()
        {
            for (int i = 0; i < heartFills.Length; i++)
            {
                if (i < PlayerController.Instance.health)
                {
                    heartFills[i].fillAmount = 1;
                }
                else
                {
                    heartFills[i].fillAmount = 0;
                }
            }
        }
        void InstantiateHeartContainers()
        {
            for (int i = 0; i < PlayerController.Instance.maxHealth; i++)
            {
                GameObject temp = Instantiate(heartContainerPrefab);
                temp.transform.SetParent(heartsParent, false);
                heartContainers[i] = temp;
                heartFills[i] = temp.transform.Find("HeartFill").GetComponent<Image>();
            }
        }
        void UpdateHeartsHUD()
        {
            SetHeartContainers();
            SetFilledHearts();
        }
    }

    FireBall:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class FireBall : MonoBehaviour
    {
        [SerializeField] float damage;
        [SerializeField] float hitForce;
        [SerializeField] int speed;
        [SerializeField] float lifetime = 1;
        // Start is called before the first frame update
        void Start()
        {
            Destroy(gameObject, lifetime);
        }
    
        private void FixedUpdate()
        {
            transform.position += speed * transform.right;
        }
        //detect hit
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.tag == "Enemy")
            {
                _other.GetComponent<Enemy>().EnemyHit(damage, (_other.transform.position - transform.position).normalized, -hitForce);
            }
        }
    }

    Enemy:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Enemy : MonoBehaviour
    {
        [SerializeField] protected float health;
        [SerializeField] protected float recoilLength;
        [SerializeField] protected float recoilFactor;
        [SerializeField] protected bool isRecoiling = false;
    
        [SerializeField] protected float speed;
    
        [SerializeField] protected float damage;
    
        protected float recoilTimer;
        protected Rigidbody2D rb;
        // Start is called before the first frame update
        protected virtual void Start()
        {
            rb = GetComponent<Rigidbody2D>();
        }
        // Update is called once per frame
        protected virtual void Update()
        {
            if (health <= 0)
            {
                Destroy(gameObject);
            }
            if (isRecoiling)
            {
                if (recoilTimer < recoilLength)
                {
                    recoilTimer += Time.deltaTime;
                }
                else
                {
                    isRecoiling = false;
                    recoilTimer = 0;
                }
            }
        }
    
        public virtual void EnemyHit(float _damageDone, Vector2 _hitDirection, float _hitForce)
        {
            health -= _damageDone;
            if (!isRecoiling)
            {
                rb.AddForce(-_hitForce * recoilFactor * _hitDirection);
            }
        }
        protected void OnCollisionStay2D(Collision2D _other)
        {
            if (_other.gameObject.CompareTag("Player") && !PlayerController.Instance.pState.invincible)
            {
                Attack();
                PlayerController.Instance.HitStopTime(0, 5, 0.5f);
            }
        }
        protected virtual void Attack()
        {
            PlayerController.Instance.TakeDamage(damage);
        }
    
    }

    Skeleton_Warrior:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Skeleton_Warrior : Enemy
    {
        // Start is called before the first frame update
        protected override void Start()
        {
            base.Start();
            rb.gravityScale = 12f;
        }
    
        // Update is called once per frame
        protected override void Update()
        {
            base.Update();
            if (!isRecoiling)
            {
                transform.position = Vector2.MoveTowards
                    (transform.position, new Vector2(PlayerController.Instance.transform.position.x, transform.position.y),
                    speed * Time.deltaTime);
            }
        }
        public override void EnemyHit(float _damageDone, Vector2 _hitDirection, float _hitForce)
        {
            base.EnemyHit(_damageDone, _hitDirection, _hitForce);
        }
    
    }
    #15872
    Terence
    Keymaster
    Helpful?
    Up
    0
    ::

    For the spell not doing damage, check if your enemies are tagged “Enemy”, because the code only damages GameObjects tagged as such.

        //detect hit
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.tag == "Enemy")
            {
                _other.GetComponent().EnemyHit(damage, (_other.transform.position - transform.position).normalized, -hitForce);
            }
        }

    For healing, you need to find where the point of failure in your Heal() function is:

        void Heal()
        {
            if (Input.GetButton("Healing") && Health < maxHealth && Mana > 0 && Grounded() && !pState.dashing)
            {
                print("Heal tick");
                pState.healing = true;
                anim.SetBool("Healing", true);
    
                //healing
                healTimer += Time.deltaTime;
                if (healTimer >= timeToHeal)
                {
                    print("Heal proc");
                    Health++;
                    healTimer = 0;
                }
    
                //drain mana
                Mana -= Time.deltaTime * manaDrainSpeed;
            }
            else
            {
                pState.healing = false;
                anim.SetBool("Healing", false);
                healTimer = 0;
            }
        }

    See in your console if the “Heal tick” and “Heal proc” messages appear.

    #15892
    Bachok
    Participant
    Helpful?
    Up
    0
    ::

    Greetings! I wrote the code and all I can say is that only the Heal tick is displayed, and then for some reason the code does not work. And I also added a few lines of code that could add more clarity to this situation and I understood that the problem is in manna, here is the code I made for Heal –

    void Heal()
    {
        print($"Healing attempt. Health: {Health}, Mana: {Mana}, Grounded: {Grounded()}, Dashing: {pState.dashing}");
        
        if (Input.GetButton("Healing") && Health < maxHealth && Mana > 0 && Grounded() && !pState.dashing)
        {
            print("Heal tick");
            pState.healing = true;
            anim.SetBool("Healing", true);
    
            //healing
            healTimer += Time.deltaTime;
            print($"healTimer: {healTimer}, timeToHeal: {timeToHeal}");
            if (healTimer >= timeToHeal)
            {
                print("Heal proc");
                Health++;
                healTimer = 0;
            }
    
            //drain mana
            Mana -= Time.deltaTime * manaDrainSpeed;
            print($"Mana after drain: {Mana}");
        }
        else
        {
            pState.healing = false;
            anim.SetBool("Healing", false);
            healTimer = 0;
            print("Healing stopped");
        }
    }

    And here is what is displayed in the console –
    19:21:00] Healing attempt. Health: 10, Mana: 10, Grounded: False, Dashing: False UnityEngine.MonoBehaviour:print (object)
    [19:21:04] Healing stopped UnityEngine.MonoBehaviour:print (object)
    [19:21:01] Healing attempt. Health: 10, Mana: 10, Grounded: True, Dashing: False UnityEngine.MonoBehaviour:print (object)
    [19:21:04] Healing attempt. Health: 9, Mana: 10, Grounded: True, Dashing: False UnityEngine.MonoBehaviour.print (object) [19:21:02] Healing attempt. Health: 9, Mana: 10, Grounded: False, Dashing: False UnityEngine.MonoBehaviour:print (object)
    [19:21:04] Heal tick UnityEngine.MonoBehaviour:print (object)
    [19:21:04] healTimer: 0.0041707, time ToHeal: 1 UnityEngine.MonoBehaviour:print (object)
    [19:21:04] Mana after drain: 1
    UnityEngine.MonoBehaviour.print (object
    I sincerely thank you for your help!!!

    #15893
    pepe cry
    Participant
    Helpful?
    Up
    0
    ::

    out of context. Can I ask that where is the Destroy after animation scripts is? Where did the part that i miss it?

    #15894
    pepe cry
    Participant
    Helpful?
    Up
    0
    ::

    sorry for out of context but can I ask that where can i find the Destroy after animation script? I has watch from the begining but still doesn’t see it

    #15895
    Bachok
    Participant
    Helpful?
    Up
    1
    ::

    Here is DestroyAfterAnimation:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class DestroyAnimation : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            Destroy(gameObject, GetComponent<Animator>().GetCurrentAnimatorStateInfo(0).length);
        }
    }
    #15896
    pepe cry
    Participant
    Helpful?
    Up
    0
    ::

    Really very appreciate your help. Hope your game finish soon

    #15900
    Chloe Lim
    Moderator
    Helpful?
    Up
    1
    ::

    Hi, there’s some things that should be checked regarding the issue of the enemy not taking damage from the fireball
    1. Enemy is using “Enemy” tag
    2. Set up a Trigger boxcollider2d on the fireball
    3. fireball has a damage value entered in
    I’ll also suggest to sort the enemy in another layer like “Attackable”

    For the healing, in your Update() function, if (pState.healing) return; is conflicting with Heal(), so can try to sort it in this way instead

    
            if (pState.dashing) return;
            RestoreTimeScale();
            FlashedWhileInvincible();
            Move();
            Heal();
            CastSpell();
    
            if (pState.healing) return;
            Flip();
            Jump();
            StartDash();
            Attack();
            Recoil();
    #15906
    Bachok
    Participant
    Helpful?
    Up
    0
    ::

    Thank you for your help, now it is not buggy and everything works!

    #15914
    Terence
    Keymaster
    Helpful?
    Up
    0
    ::

    Glad Chloe was able to help you Bachok.

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

Go to Login Page →


Advertisement below: