Forum begins after the advertisement:


[Part 6] Error on bat code.

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [Part 6] Error on bat code.

Viewing 8 posts - 1 through 8 (of 8 total)
  • Author
    Posts
  • #18863
    Tyler Fruge
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    I keep getting an error that says: “Assets\Scripts\Battacker.cs(54,26): error CS0115: ‘Battacker.EnemyGetsHit(float, Vector2, float)’: no suitable method found to override” (Battacker is what I named my bat.)

    Here is my code:

    Battacker Script:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Battacker : Enemy
    {
        [SerializeField] private float chaseDistance;
    
        float timer;
    
        // Start is called before the first frame update
        protected override void Start()
        {
            base.Start();
            ChangeState(EnemyStates.Battacker_Idle);
        }
    
        protected override void UpdateEnemyStates()
        {
            float _dist = Vector2.Distance(transform.position, PlayerController.Instance.transform.position);
    
            switch (currentEnemyState)
            {
                case EnemyStates.Battacker_Idle:
                    if (_dist < chaseDistance)
                    {
                        ChangeState(EnemyStates.Battacker_Chase);
                    }
                    break;
    
                case EnemyStates.Battacker_Chase:
                    rb.MovePosition(Vector2.MoveTowards(transform.position, PlayerController.Instance.transform.position, Time.deltaTime * speed));
    
                    FlipBattacker();
                    break;
    
                case EnemyStates.Battacker_Stunned:
                    timer += Time.deltaTime;
    
                    if(timer > stunDuration)
                    {
                        ChangeState(EnemyStates.Battacker_Idle);
                        timer = 0;
                    }
                    break;
    
                case EnemyState.Battacker_Death:
    
    
                    break;
            }
        }
    
        public override void EnemyGetsHit(float _damageDone, Vector2 _hitDirection, float _hitForce)
        {
            base.EnemyGetsHit(_damageDone, _hitDirection, _hitForce);
    
            if(health > 0)
            {
                ChangeState(EnemyStates.Battacker_Stunned);
            }
            else
            {
                ChangeState(EnemyStates.Battacker_Death);
            }
        }
        void FlipBattacker()
        {
            sr.flipX = PlayerController.Instance.transform.position.x < transform.position.x;
        }
    }

    Enemy Script:

    using System.Collections;
    using System.Collections.Generic;
    using Unity.VisualScripting.Antlr3.Runtime.Tree;
    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;
        protected SpriteRenderer sr;
    
        protected enum EnemyStates
        {
            //Cadaver
            Cadaver_Idle,
            Cadaver_Flip,
    
    
            //Battacker
            Battacker_Idle,
            Battacker_Chase,
            Battacker_Stunned,
            Battacker_Death,
    
        }
        protected EnemyStates currentEnemyState;
    
        // Start is called before the first frame update
        protected virtual void Start()
        {
            rb = GetComponent<Rigidbody2D>();
            sr = GetComponent<SpriteRendering>();
        }
        // Update is called once per frame
        protected virtual void Update()
        {
    
            if (isRecoiling)
            {
                if (recoilTimer < recoilLength)
                {
                    recoilTimer += Time.deltaTime;
                }
                else
                {
                    isRecoiling = false;
                    recoilTimer = 0;
                }
            }
            else
            {
                UpdateEnemyStates();
            }
        }
    
    
    
        public virtual void EnemyHit(float _damageDone, Vector2 _hitDirection, float _hitForce)
        {
            health -= _damageDone;
            if (!isRecoiling)
            {
                rb.velocity = (-_hitForce * recoilFactor * _hitDirection);
                isRecoiling = true;
            }
        }
        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 UpdateEnemyStates() { }
    
        protected void ChangeState(EnemyStates _newState)
        {
            currentEnemyState = _newState;
        }
    
        protected virtual void Attack()
        {
            PlayerController.Instance.TakeDamage(damage);
        }
    }

    Player Controller Script:

    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 float jumpBufferCounter = 0; //stores the jump button input
        [SerializeField] private float 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
    
        [SerializeField] private float timeBetweenAttack;
        private float timeSinceAttack;
    
        [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.1f;
        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;
        float castOrHealTimer;
        [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;
        private bool canFlash = true;
    
        //creates a singleton of the PlayerController
        public static PlayerController Instance;
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
            Health = maxHealth;
        }
    
    
        // 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;
    
            Health = maxHealth;
            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();
    
            RestoreTimeScale();
            if (pState.dashing) return;
    
            FlashWhileInvincible();
            Move();
            Heal();
            CastSpell();
            if (pState.healing) return;
            Flip();
            Jump();
            StartDash();
            Attack();
        }
        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 || pState.healing) return;
            Recoil();
        }
    
        void GetInputs()
        {
            xAxis = Input.GetAxisRaw("Horizontal");
            yAxis = Input.GetAxisRaw("Vertical");
            attack = Input.GetButtonDown("Attack");
    
            if (Input.GetButton("Cast/Heal"))
            {
                castOrHealTimer += Time.deltaTime;
            }
    
        }
    
        void Flip()
        {
            if (xAxis < 0)
            {
                transform.localScale = new Vector2(-Mathf.Abs(transform.localScale.x), transform.localScale.y);
                pState.lookingRight = false;
            }
            else if (xAxis > 0)
            {
                transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x), transform.localScale.y);
                pState.lookingRight = true;
            }
        }
    
        private void Move()
        {
            if (pState.healing || pState.casting) rb.velocity = new Vector2(0, rb.velocity.y);
            else 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);
            yield return new WaitForSeconds(dashTime);
            rb.gravityScale = gravity;
            pState.dashing = false;
            yield return new WaitForSeconds(dashCooldown);
            canDash = true;
        }
    
        public IEnumerator WalkIntoNewScene(Vector2 _exitDir, float _delay)
        {
            pState.invincible = true;
            Debug.Log("Walking into new Scene cutscene begin.");
            //If exit direction is upwards
            if (_exitDir.y > 0)
            {
                rb.velocity = jumpForce * _exitDir;
            }
    
            //If exit direction requires horizontal movement
            if (_exitDir.x != 0)
            {
                xAxis = _exitDir.x > 0 ? 1 : -1;
                Move();
            }
    
            Flip();
            yield return new WaitForSeconds(_delay);
            pState.invincible = false;
            pState.cutscene = false;
            Debug.Log("Walking into new Scene cutscene end.");
        }
    
        void Attack()
        {
            timeSinceAttack += Time.deltaTime;
            if (attack && timeSinceAttack >= timeBetweenAttack)
            {
                timeSinceAttack = 0;
                anim.SetTrigger("Attacking");
    
                if (yAxis == 0 || yAxis < 0 && Grounded())
                {
                    int _recoilLeftOrRight = pState.lookingRight ? 1 : -1;
    
                    Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX, Vector2.right * _recoilLeftOrRight, recoilXSpeed);
                    Instantiate(slashEffect, SideAttackTransform);
                }
                else if (yAxis > 0)
                {
                    Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, Vector2.up, recoilYSpeed);
                    SlashEffectAtAngle(slashEffect, 80, UpAttackTransform);
                }
                else if (yAxis < 0 && !Grounded())
                {
                    Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, Vector2.down, recoilYSpeed);
                    SlashEffectAtAngle(slashEffect, -90, DownAttackTransform);
                }
            }
    
    
        }
        void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool, Vector2 _recoilDir, float _recoilStrength)
        {
            Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer);
    
            if (objectsToHit.Length > 0)
            {
                _recoilBool = true;
            }
            for (int i = 0; i < objectsToHit.Length; i++)
            {
                if (objectsToHit[i].GetComponent<Enemy>() != null)
                {
                    objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, _recoilDir, _recoilStrength);
    
    
                        if (objectsToHit[i].CompareTag("Enemy"))
                    {
    
                        Mana += manaGain;       
                    }
                }
            }
        }
        void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle, Transform _attackTransform)
        {
            _slashEffect = Instantiate(_slashEffect, _attackTransform);
            _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
            _slashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y);
        }
        void Recoil()
        {
            if (pState.recoilingX)
            {
                if (pState.lookingRight)
                {
                    rb.velocity = new Vector2(-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 WaitForSeconds(1f);
            pState.invincible = false;
        }
    
        IEnumerator Flash()
        {
            sr.enabled = !sr.enabled;
            canFlash = false;
            yield return new WaitForSeconds(0.2f);
            canFlash = true;
        }
    
        void FlashWhileInvincible()
        {
            if (pState.invincible)
            {
                if (Time.timeScale > 0.2 && canFlash)
                {
                    StartCoroutine(Flash());
                }
            }
            else
            {
                sr.enabled = true;
            }
        }
        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("Cast/Heal") && castOrHealTimer > 0.2f && 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.GetButtonUp("Cast/Heal") && castOrHealTimer <= 0.2f && timeSinceCast >= timeBetweenCast && Mana >= manaSpellCost)
            {
                pState.casting = true;
                timeSinceCast = 0;
                StartCoroutine(CastCoroutine());
            }
            else
            {
                timeSinceCast += Time.deltaTime;
            }
            if (!Input.GetButton("Cast/Heal"))
            {
                castOrHealTimer = 0;
            }
            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 WaitForSeconds(0.1f);
    
            //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 WaitForSeconds(0.15f);
            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 = jumpBufferCounter - Time.deltaTime * 10;
            }
        }
    }
    #18865
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    The Enemy script only has an EnemyHit() function. There is no EnemyGetsHit(), so when you try to override that in Battacker it fails.

    Try renaming your function and see if it works thereafter.

    #18866
    Tyler Fruge
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    ok so that did help, thanks, and i also found more errors in the script, but fixed them. but now the bat enemy is stuck in the idle animation and it won’t change to any other state.

    View post on imgur.com

    Battacker Code:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Battacker : Enemy
    {
        [SerializeField] private float chaseDistance;
    
        [SerializeField] private float stunDuration;
        float timer;
    
        // Start is called before the first frame update
        protected override void Start()
        {
            base.Start();
            ChangeState(EnemyStates.Battacker_Idle);
        }
    
        protected override void UpdateEnemyStates()
        {
            float _dist = Vector2.Distance(transform.position, PlayerController.Instance.transform.position);
    
            switch (GetCurrentEnemyState)
            {
                case EnemyStates.Battacker_Idle:
                    rb.velocity = new Vector2(0, 0);
    
                    if (_dist < chaseDistance)
                    {
                        ChangeState(EnemyStates.Battacker_Chase);
                    }
                    break;
    
                case EnemyStates.Battacker_Chase:
                    rb.MovePosition(Vector2.MoveTowards(transform.position, PlayerController.Instance.transform.position, Time.deltaTime * speed));
    
                    FlipBattacker();
                    if(_dist > chaseDistance)
                    {
                        ChangeState(EnemyStates.Battacker_Idle);
                    }
                    break;
    
                case EnemyStates.Battacker_Stunned:
                    timer += Time.deltaTime;
    
                    if(timer > stunDuration)
                    {
                        ChangeState(EnemyStates.Battacker_Idle);
                        timer = 0;
                    }
                    break;
    
                case EnemyStates.Battacker_Death:
                Death(Random.Range(5, 10));
    
                    break;
            }
        }
    
        public override void EnemyHit(float _damageDone, Vector2 _hitDirection, float _hitForce)
        {
            base.EnemyHit(_damageDone, _hitDirection, _hitForce);
    
            if (health > 0)
            {
                ChangeState(EnemyStates.Battacker_Stunned);
            }
            else
            {
                ChangeState(EnemyStates.Battacker_Death);
    
            }
        }
    
        protected override void Death(float _destroyTime)
        {
            rb.gravityScale = 12;
            base.Death(_destroyTime);
        }
    
        protected override void ChangeCurrentAnimation()
        {
            anim.SetBool("Idle", GetCurrentEnemyState == EnemyStates.Battacker_Idle);
    
            anim.SetBool("Chase", GetCurrentEnemyState == EnemyStates.Battacker_Chase);
    
            anim.SetBool("Stunned", GetCurrentEnemyState == EnemyStates.Battacker_Stunned);
    
            if (GetCurrentEnemyState == EnemyStates.Battacker_Death)
            {
                anim.SetTrigger("Death");
            }
    
        }
        void FlipBattacker()
        {
            sr.flipX = PlayerController.Instance.transform.position.x < transform.position.x;
        }
    }

    Enemy Code:

    using System.Collections;
    using System.Collections.Generic;
    using Unity.VisualScripting.Antlr3.Runtime.Tree;
    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;
        [SerializeField] protected GameObject orangeBlood;
    
    
        protected float recoilTimer;
        protected Rigidbody2D rb;
        protected SpriteRenderer sr;
        protected Animator anim;
    
        protected enum EnemyStates
        {
            //Cadaver
            Cadaver_Idle,
            Cadaver_Flip,
    
    
            //Battacker
            Battacker_Idle,
            Battacker_Chase,
            Battacker_Stunned,
            Battacker_Death,
    
            //Spooktergeist
            Spootergeist_Idle,
            Spooktergeist_Chase,
            Spooktergeist_Stunned,
            Spooktergeist_Death,
    
        }
        protected EnemyStates GetCurrentEnemyState;
        protected virtual EnemyStates GetGetCurrentEnemyState
        {
            get { return GetCurrentEnemyState; }
            set
            {
                if (GetCurrentEnemyState != value)
                {
                    GetCurrentEnemyState = value;
    
                    ChangeCurrentAnimation();
                }
            }
        }
    
        // Start is called before the first frame update
        protected virtual void Start()
        {
            rb = GetComponent<Rigidbody2D>();
            sr = GetComponent<SpriteRenderer>();
            anim = GetComponent<Animator>();
        }
        // Update is called once per frame
        protected virtual void Update()
        {
    
            if (isRecoiling)
            {
                if (recoilTimer < recoilLength)
                {
                    recoilTimer += Time.deltaTime;
                }
                else
                {
                    isRecoiling = false;
                    recoilTimer = 0;
                }
            }
            else
            {
                UpdateEnemyStates();
            }
        }
    
    
    
        public virtual void EnemyHit(float _damageDone, Vector2 _hitDirection, float _hitForce)
        {
            health -= _damageDone;
            if (!isRecoiling)
            {
                GameObject _orangeBlood = Instantiate(orangeBlood, transform.position, Quaternion.identity);
                Destroy(_orangeBlood, 5.5f);
                rb.velocity = (-_hitForce * recoilFactor * _hitDirection);
    
            }
        }
        protected void OnCollisionStay2D(Collision2D _other)
        {
            if (_other.gameObject.CompareTag("Player") && !PlayerController.Instance.pState.invincible && !PlayerController.Instance.pState.invincible && health > 0)
            {
                Attack();
                PlayerController.Instance.HitStopTime(0, 5, 0.5f);
            }
        }
    
        protected virtual void Death(float _destroyTime)
        {
            Destroy(gameObject, _destroyTime);
        }
    
        protected virtual void UpdateEnemyStates() { }
        protected virtual void ChangeCurrentAnimation() { }
    
        protected void ChangeState(EnemyStates _newState)
        {
            GetCurrentEnemyState = _newState;
        }
    
        protected virtual void Attack()
        {
            PlayerController.Instance.TakeDamage(damage);
        }
    }
    #18867
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Can you open the Animator window on a different dock and select the Bat? Then, play the game and see how the animations in the Animator window for the bat flows and take a video for it.

    We need to look at the Animator window to see how the animation flows and where it is getting stuck.

    #18868
    Tyler Fruge
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    ok, here is the link:

    View post on imgur.com
    #18869
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    The Idle animation is only able to transit to the Chase animation based on your animator setup. So you need to see what the transition condition of your Idle -> Chase is by clicking on the arrow.

    It is a little difficult to explain through text, but if you are not able to figure it out, send your project over to me and I will go through it on stream.

    #18870
    #18874
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Fixed your issue:

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: