Forum begins after the advertisement:


Boss Disappearing When Touching Ground

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity Boss Disappearing When Touching Ground

Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #16495
    Payton
    Level 12
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    Hello,

    I am having an issue where when the Boss touches the ground it seems to disappear, yet I can see that its Y value is going down. So it’s falling, but I can’t seem to figure out why its disappearing, or why its falling. Any help would be appreciated.

    Here is the Code for my Boss Script:

    <code>using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using UnityEngine;
    
    public class Boss : Enemy
    {
        public static Boss Instance;
    
        [SerializeField] GameObject slashEffect;
        public Transform SideAttackTransform; //the middle of the side attack area
        public Vector2 SideAttackArea; //how large the area of side attack is
    
        public Transform UpAttackTransform; //the middle of the up attack area
        public Vector2 UpAttackArea; //how large the area of side attack is
    
        public Transform DownAttackTransform; //the middle of the down attack area
        public Vector2 DownAttackArea; //how large the area of down attack is
    
        public float attackRange;
        public float attackTimer;
    
        [HideInInspector] public bool facingRight;
    
        [Header("Ground Check Settings:")]
        public 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
    
        int hitCounter;
        bool stunned, canStun;
        bool alive;
    
        [HideInInspector] public float runSpeed;
    
        public GameObject impactParticle;
    
        [HideInInspector] public bool attacking;
        [HideInInspector] public float attackCountdown;
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
        }
    
        // Start is called before the first frame update
        protected override void Start()
        {
            base.Start();
            sr = GetComponentInChildren<SpriteRenderer>();
            anim = GetComponentInChildren<Animator>();
        }
    
        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;
            }
        }
    
        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
        protected override void Update()
        {
            base.Update();
    
            if (!attacking)
            {
                attackCountdown -= Time.deltaTime;
            }
        }
    
        public void Flip()
        {
            if (playerController.Instance.transform.position.x < transform.position.x && transform.localScale.x > 0)
            {
                transform.eulerAngles = new Vector2(transform.eulerAngles.x, 180);
                facingRight = false;
            }
            else
            {
                transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0);
                facingRight = true;
            }
        }
    
        protected override void UpdateEnemyStates()
        {
            if (playerController.Instance != null)
            {
                switch (GetCurrentEnemyState)
                {
                    case EnemyStates.Boss_Stage1:
                        break;
    
                    case EnemyStates.Boss_Stage2:
                        break;
    
                    case EnemyStates.Boss_Stage3:
                        break;
    
    
                }
            }
        }
    
        protected override void OnCollisionStay2D(Collision2D _other) { }
    
        public void AttackHandler()
        {
            if (currentEnemyState == EnemyStates.Boss_Stage1)
            {
                if (Vector2.Distance(playerController.Instance.transform.position, rb.position) <= attackRange)
                {
                    StartCoroutine(TripleSlash());
                }
            }
        }
    
        public void ResetAllAttacks()
        {
            attacking = false;
    
            StopCoroutine(TripleSlash());
        }
    
        IEnumerator TripleSlash()
        {
            attacking = true;
            rb.velocity = Vector2.zero;
    
            anim.SetTrigger("Slash");
            SlashAngle();
            yield return new WaitForSeconds(0.3f);
            anim.ResetTrigger("Slash");
    
            anim.SetTrigger("Slash");
            SlashAngle();
            yield return new WaitForSeconds(0.5f);
            anim.ResetTrigger("Slash");
    
            anim.SetTrigger("Slash");
            SlashAngle();
            yield return new WaitForSeconds(0.2f);
            anim.ResetTrigger("Slash");
    
            ResetAllAttacks();
        }
    
        void SlashAngle()
        {
            if (playerController.Instance.transform.position.x - transform.position.x != 0)
            {
                Instantiate(slashEffect, SideAttackTransform);
            }
            else if (playerController.Instance.transform.position.y > transform.position.y)
            {
                SlashEffectAtAngle(slashEffect, 80, UpAttackTransform);
            }
            else if (playerController.Instance.transform.position.y < transform.position.y)
            {
                SlashEffectAtAngle(slashEffect, -90, UpAttackTransform);
            }
        }
    
        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);
        }
    }
    
    </code>

    Here is the code for my Idle:

    <code>using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Boss_Idle : StateMachineBehaviour
    {
        Rigidbody2D rb;
        // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
        override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            rb = animator.GetComponentInParent<Rigidbody2D>();
        }
    
        // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
        override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            rb.velocity = Vector2.zero;
            RunToPlayer(animator);
    
            if (Boss.Instance.attackCountdown <= 0)
            {
                Boss.Instance.AttackHandler();
                Boss.Instance.attackCountdown = UnityEngine.Random.Range(Boss.Instance.attackTimer - 1, Boss.Instance.attackTimer + 1);
            }
    
            if (!Boss.Instance.Grounded())
            {
                rb.velocity = new Vector2(rb.velocity.x, -25); //if knight is not grounded, fall to ground
            }
        }
    
        void RunToPlayer(Animator animator)
        {
            if (Vector2.Distance(playerController.Instance.transform.position, rb.position) >= Boss.Instance.attackRange)
            {
                animator.SetBool("Run", true);
            }
            else
            {
                return;
            }
        }
        // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
        override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
    
        }
    }</code>

    And here is my code for Run:

    <code>using System;
    using UnityEngine;
    
    public class Boss_Run : StateMachineBehaviour
    {
        Rigidbody2D rb;
        // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
        override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            rb = animator.GetComponentInParent<Rigidbody2D>();
        }
    
        // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
        override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            TargetPlayerPosition(animator);
    
            if (Boss.Instance.attackCountdown <= 0)
            {
                Boss.Instance.AttackHandler();
                Boss.Instance.attackCountdown = UnityEngine.Random.Range(Boss.Instance.attackTimer - 1, Boss.Instance.attackTimer + 1);
            }
        }
    
        void TargetPlayerPosition(Animator animator)
        {
            if (Boss.Instance.Grounded())
            {
                Boss.Instance.Flip();
                Vector2 _target = new Vector2(playerController.Instance.transform.position.x, rb.position.y);
                Vector2 _newPos = Vector2.MoveTowards(rb.position, _target, Boss.Instance.runSpeed * Time.fixedDeltaTime);
                Boss.Instance.runSpeed = Boss.Instance.speed;
                rb.MovePosition(_newPos);
            }
            else
            {
                rb.velocity = new Vector2(rb.velocity.x, -25); //if knight is not grounded, fall to ground
            }
            if (Vector2.Distance(playerController.Instance.transform.position, rb.position) <= Boss.Instance.attackRange)
            {
                animator.SetBool("Run", false);
            }
            else
            {
                return;
            }
        }
    
        //OnStateExit is called when a transition ends and the state machine finishes evaluating this state
        override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            animator.SetBool("Run", false);
        }
    
    }
    </code>

    Here is an image of the BossHandler panel and a video of the issue:

    View post on imgur.com
    #16496
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    1
    ::

    This line in Boss_Idle is likely the problem:

        // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
        override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            rb.velocity = Vector2.zero;
            RunToPlayer(animator);
    
            if (Boss.Instance.attackCountdown <= 0)
            {
                Boss.Instance.AttackHandler();
                Boss.Instance.attackCountdown = UnityEngine.Random.Range(Boss.Instance.attackTimer - 1, Boss.Instance.attackTimer + 1);
            }
    
            if (!Boss.Instance.Grounded())
            {
                rb.velocity = new Vector2(rb.velocity.x, -25); //if knight is not grounded, fall to ground
            }
        }

    Change it to this:

    rb.velocity = new Vector2(rb.velocity.x, -25) * Time.deltaTime;

    This will reduce the fall speed of your boss, and prevent high frame rates from causing your boss to travel downwards extremely quickly.

    If you need to move at high speeds, set the Rigidbody to have Continuous Collision.

    Check this article out if you don’t know what Continuous Collision is: https://blog.terresquall.com/2019/12/collision-detection-modes-in-unitys-rigidbody-component/

      1 anonymous person
    has upvoted this post.
    #16504
    Payton
    Level 12
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    I just tried that, however it didn’t seem to work. As the Boss Handler is still falling and the boss still “Disappears”.

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

    Try disabling the Boss_Idle script when the ground is touched and see if the issue is still there. If it is, try disabling another one of the scripts as well. Do this until you find the script that is the culprit.

    if (!Boss.Instance.Grounded())
    {
        enabled = false;
        return;
        rb.velocity = new Vector2(rb.velocity.x, -25); //if knight is not grounded, fall to ground
    }

    Here’s how you can disable other scripts:

    if (!Boss.Instance.Grounded())
    {
        GetComponentInChildren<Boss_Run>().enabled = false;
        return;
        rb.velocity = new Vector2(rb.velocity.x, -25); //if knight is not grounded, fall to ground
    }
    #16517
    Payton
    Level 12
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    Okay, so that also didn’t seem to work as I think “enabled = false; return;” Only works on MonoBehaviour. So I instead Changed some things around on my objects. I put the rigidbody, box collider, and the script on the Boss rather than the Boss handler. This fixed the falling through the floor issue. Then I adjusted the getcomponetinparent to just getcompenent. It now plays the Run animation, yet the player does not move, even when I go into the modifiers and adjust their speed.

    #16524
    Payton
    Level 12
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    Sorry- its not the player that’s not moving, its the boss.

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

    Yes, this line disables the Idle / Run script.

    GetComponentInChildren<Boss_Run>().enabled = false;

    Never mind, restore the original code. Did you try setting the Rigidbody’s Collision Mode to Continuous?

    #16542
    Payton
    Level 12
    Silver Supporter (Patron)
    Helpful?
    Up
    1
    ::

    Yeah, I checked and it still didn’t work- but I did get it to work eventually! I was able to get the boss to move by adding code that essentially forced it to move if the velocity was 0 at the start. This worked out pretty well, and it’s now working like how I want it to :D Thanks for the help tho! its very much appreciated!

    has upvoted this post.
    #16543
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    No problem Payton. Glad to help!

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

Go to Login Page →


Advertisement below: