Forum begins after the advertisement:


[Part 10] Boss hit very fast and Null ref

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [Part 10] Boss hit very fast and Null ref

Viewing 20 posts - 1 through 20 (of 27 total)
  • Author
    Posts
  • #16384
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    So i was making the boss for the game. And when I making the boss event and test the 3 hit function. My boss hit very fast. As we could said it doesn’t reset the animator. It just 2 punch in a frame. And when the boss hit, the console send a message of NullReference. As you can see in what i show below. First is the test video

    then is the code. First is the CorruptedMonk.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CorruptedMonk : Enemy
    {
        public static CorruptedMonk Instance;
    
        [Header("Attacking setting")]
        [SerializeField] public Transform sideAttackTransform, upAttackTransform, downAttackTransform;
        [SerializeField] public Vector2 sideAttackArea, upAttackArea, downAttackArea;
        [Space(5)]
    
        public float attackRange;
        public float attackTimer;
    
        [Header("Groundcheck setting")]
        [SerializeField] private Transform groundcheck;
        [SerializeField] private float groundchecky = 0.2f;
        [SerializeField] private float groundcheckx = 0.5f;
        [SerializeField] private LayerMask isground;
    
        int hitCounter;
        bool stunned, canStunned;
        bool alive;
    
        [HideInInspector] public float runSpeed;
        [HideInInspector] public bool facingRight;
        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>();
            ChangeState(EnemyStates.Monk_State1);
            alive = true;
        }
    
        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;
            }
        }
    
        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
        protected override void Update()
        {
            base.Update();
            if (!attacking)
            {
                attackCountdown -= Time.deltaTime;
            }
        }
    
        public void Flip()
        {
            if (Move.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(Move.Instance != null)
            {
                switch (GetCurrentEnemyStates)
                {
                    case EnemyStates.Monk_State1:
                        break;
                    case EnemyStates.Monk_State2:
                        break;
                    case EnemyStates.Monk_State3: 
                        break;
                    case EnemyStates.Monk_State4: 
                        break;
                }
            } 
        }
    
        protected override void OnCollisionStay2D(Collision2D _other)
        {
    
        }
    
        public void ResetAllAttack()
        {
            attacking = false;
            StopCoroutine(TriplePunch());
        }
    
        #region attacking
        #region variables
        [HideInInspector] public bool attacking;
        [HideInInspector] public float attackCountdown;
        #endregion
    
        #region Control
    
        public void AttackHandler()
        {
            if(currentState == EnemyStates.Monk_State1)
            {
                if(Vector2.Distance(Move.Instance.transform.position,rb.position) <= attackRange)
                {
                    StartCoroutine(TriplePunch());
                }
                else
                {
                    return;
                }
            }
        }
    
        #endregion
    
        #region Stage1
        IEnumerator TriplePunch()
        {
            attacking = true;
            rb.velocity = Vector2.zero;
            anim.SetTrigger("Punch");
            yield return new WaitForSeconds(0.3f);
            anim.ResetTrigger("Punch");
    
            anim.SetTrigger("Punch");
            yield return new WaitForSeconds(0.5f);
            anim.ResetTrigger("Punch");
    
            anim.SetTrigger("Punch");
            yield return new WaitForSeconds(0.1f);
            anim.ResetTrigger("Punch");
    
            ResetAllAttack();
        }
    
        #endregion
        #endregion
    
    }

    Then is the Corrupted_Monk_Event.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Corrupted_Monk_Event : MonoBehaviour
    {
        void PunchDamagePlayer()
        {
            if (Move.Instance.transform.position.x > transform.position.x || Move.Instance.transform.position.x < transform.position.x)
            {
                Hit(CorruptedMonk.Instance.sideAttackTransform, CorruptedMonk.Instance.sideAttackArea);
            }
            else if (Move.Instance.transform.position.y > transform.position.y)
            {
                Hit(CorruptedMonk.Instance.upAttackTransform, CorruptedMonk.Instance.upAttackArea);
            }
            else if (Move.Instance.transform.position.y < transform.position.y)
            {
                Hit(CorruptedMonk.Instance.downAttackTransform, CorruptedMonk.Instance.downAttackArea);
            }
        }
        void Hit(Transform _attackTrasnform, Vector2 _attackArea)
        {
            Collider2D[] _objectToHit = Physics2D.OverlapBoxAll(_attackTrasnform.position, _attackArea, 0);
            for(int i = 0 ; i < _objectToHit.Length; i++)
            {
                if (_objectToHit[i].GetComponent<Collider2D>() != null)
                {
                    _objectToHit[i].GetComponent<Move>().takeDmg(CorruptedMonk.Instance.dmgMake);
                }
            }
        }
    }
    Null ref error

    I even try to change from WaitForSecond to WaitForSecondRealTime but still doesn’t making the punch slow down. Tks you for the series. And another thank for helping me

    #16385
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    Update: I add a debug to check how many punch the boss does and it only make 3 punch. So the Boss still hitting 3 hit. But i got 6 damage and the boss doesn’t need to reset animation at all

    null and the null ref i already fix it. The getcomponent at the event first getcomponent i just took it wrong. From PlayerController to collider2d. It my fault. It kinda late in the time I made it so maybe my eyes has lied me.
    #16399
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    I really need help here. I making the diving pillar skill and the boss. It jump to the sky and dive down. But when he create the pilar. I only see that he only create one pillar only. And the pillar making multiple time damage to my player and my player get oneshot instead of only two damaga. If you need me to provide anything, just reply and I will give it ASAP.

    #16449
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Let’s start by fixing the NullReferenceException here:

    NullReferenceException

    The issue appears to be with line 29 of the Corrupted_Monk_Event script:

    public class Corrupted_Monk_Event : MonoBehaviour
    {
        void PunchDamagePlayer()
        {
            if (Move.Instance.transform.position.x > transform.position.x || Move.Instance.transform.position.x < transform.position.x)
            {
                Hit(CorruptedMonk.Instance.sideAttackTransform, CorruptedMonk.Instance.sideAttackArea);
            }
            else if (Move.Instance.transform.position.y > transform.position.y)
            {
                Hit(CorruptedMonk.Instance.upAttackTransform, CorruptedMonk.Instance.upAttackArea);
            }
            else if (Move.Instance.transform.position.y < transform.position.y)
            {
                Hit(CorruptedMonk.Instance.downAttackTransform, CorruptedMonk.Instance.downAttackArea);
            }
        }
        void Hit(Transform _attackTrasnform, Vector2 _attackArea)
        {
            Collider2D[] _objectToHit = Physics2D.OverlapBoxAll(_attackTrasnform.position, _attackArea, 0);
            for(int i = 0 ; i < _objectToHit.Length; i++)
            {
                if (_objectToHit[i].GetComponent<Collider2D>() != null)
                {
                    _objectToHit[i].GetComponent<Move>().takeDmg(CorruptedMonk.Instance.dmgMake);
                }
            }
        }
    }

    Do you have a Move component attached to your boss?

    #16464
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    So about the null references, I has already fix it but forgot to write the update on here. it was my mistake that calling this _objectToHit[i].GetComponent<Collider2D>() instead of _objectToHit[i].GetComponent<Move>() I has change and it look good. Now the problem is only the pillar doesn’t spawn right and the dmg it make. I will show you a record of it

    As you could see in the record. The pillar is spawn in a random patten and sometime move very far the player. But when it hit, it make about 10 time hits that you could see in the console in just a tic. Here is the script of the water pillar Diving pillar.cs

    public class DivingPillar : MonoBehaviour
    {
        // Start is called before the first frame update
        private void OnTriggerEnter2D(Collider2D collision)
        {
            if (collision.CompareTag("Player"))
            {
                Debug.Log("Player hit by pillar!");
                collision.GetComponent<Move>().takeDmg(CorruptedMonk.Instance.dmgMake);
                Debug.Log($"Player health after hit: {collision.GetComponent<Move>().health}");
            }
        }
    
    }

    boss_flyingkick.cs in the flyingkick animator

    public class Boss_FlyingKick : StateMachineBehaviour
    {
        Rigidbody2D rb;
        bool callOnce;
        // 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)
        {
            CorruptedMonk.Instance.divingCollider.SetActive(true);
    
            if (CorruptedMonk.Instance.isonGround())
            {
                CorruptedMonk.Instance.divingCollider.SetActive(false);
                if (!callOnce)
                {
                    CorruptedMonk.Instance.DivingPillar();
                    animator.SetBool("FlyingKick", false);
                    CorruptedMonk.Instance.ResetAllAttack();
                    callOnce = true;
                }
            }
        }
    
        // 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)
        {
            callOnce = false;
        }
    
    }

    Boss_jump.cs

    public class Boss_Jump : 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)
        {
            DiveAttack();
        }
    
        void DiveAttack()
        {
            if (CorruptedMonk.Instance.flyingKichAttack)
            {
                CorruptedMonk.Instance.Flip();
    
                Vector2 _newPos = Vector2.MoveTowards(rb.position, CorruptedMonk.Instance.moveToPosition, CorruptedMonk.Instance.speed * 2 * Time.fixedDeltaTime);
                rb.MovePosition(_newPos);
    
                if (CorruptedMonk.Instance.touchWall())
                {
                    CorruptedMonk.Instance.moveToPosition.x = rb.position.x;
                    _newPos = Vector2.MoveTowards(rb.position, CorruptedMonk.Instance.moveToPosition, CorruptedMonk.Instance.speed * 3 * Time.fixedDeltaTime);
                }
    
                float _distance = Vector2.Distance(rb.position, _newPos);
                if(_distance <0.1f)
                {
                    CorruptedMonk.Instance.Dive();
                }
            }
        }
        // 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)
        {
    
        }
    
    }

    and the flying kick in the boss script

    void FlyingKickAttackJump()
    {
        attacking = true ;
        moveToPosition = new Vector2(Move.Instance.transform.position.x, rb.velocity.y +2);
        flyingKichAttack = true ;
        anim.SetBool("Jump", true);
    }
    
    public void Dive()
    {
        anim.SetBool("FlyingKick",true);
        anim.SetBool("Jump", false);
    }
    
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.GetComponent<Move>() != null && flyingKichAttack)
        {
            collision.GetComponent<Move>().takeDmg(dmgMake * 2);
            Move.Instance.playerStateList.recoilingX = true;
    
            // Disable the attack to prevent further damage
            flyingKichAttack = false;
        }
    }
    
    public void DivingPillar()
    {
        if (pillar == null)
        {
            Debug.LogError("Pillar prefab is missing!");
            return;
        }
    
        Vector2 _impactPoint = groundcheck.position;
        float _spawnDistance = 3;
    
        for (int i = 0; i < 10; i++)
        {
            Vector2 _pillarSpawnPointRight = _impactPoint + new Vector2(_spawnDistance, 0);
            Vector2 _pillarSpawnPointLeft = _impactPoint - new Vector2(_spawnDistance, 0);
    
            Debug.Log($"Spawning right pillar at: {_pillarSpawnPointRight}");
            Instantiate(pillar, _pillarSpawnPointRight, Quaternion.Euler(0, 0, 0));
            Debug.Log($"Spawning left pillar at: {_pillarSpawnPointLeft}");
            Instantiate(pillar, _pillarSpawnPointLeft, Quaternion.Euler(0, 0, 0));
    
            _spawnDistance += 2;
        }
    
        ResetAllAttack();
    }
    #16465
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    It seems like all your diving pillars are spawning at the same place. Can you check your Hierarchy to confirm this? Then we will find out why it is.

    Add the following as well and show me what it prints on the console:

        for (int i = 0; i < 10; i++)
        {
            Vector2 _pillarSpawnPointRight = _impactPoint + new Vector2(_spawnDistance, 0);
            Vector2 _pillarSpawnPointLeft = _impactPoint - new Vector2(_spawnDistance, 0);
            print($"Diving Pillar {i} has a spawn distance of {_spawnDistance}");
            Debug.Log($"Spawning right pillar at: {_pillarSpawnPointRight}");
            Instantiate(pillar, _pillarSpawnPointRight, Quaternion.Euler(0, 0, 0));
            Debug.Log($"Spawning left pillar at: {_pillarSpawnPointLeft}");
            Instantiate(pillar, _pillarSpawnPointLeft, Quaternion.Euler(0, 0, 0));
    
            _spawnDistance += 2;
        }
    #16467
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    In the hierachy, yes the boss spawn so much water pillar. As you can see at the below picture

    Boss hierachy And in the console, this is what it show Console It up to 9 pillar and the spawn distance of 21
    #16469
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    I forgot to write this in previous reply but yes, what you thinkk is right, all the pillar is at the same place. There about more than 15 pillar and I check every single one and it appear in the same place. Stack together

    #16474
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Can you check all the Pillars in the Hierarchy and see if they are in the same coordinates? Because in our code we created them in different places, but they are still stacking in the same place.

    If we find out why, we will fix your issue.

    #16477
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    yes, in the inspector, it was the same value in the transform. All have the same value like this when the boss create the pillar

    boss pillar inspector
    #16500
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Does your console have the “Spawning left pillar at…” and “Spawning right pillar at…” messages?

    #16502
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    Yes, i have. I was thinking that the number it show up in the console is the x and y position of the pillar. But then when we figure out the pillar is spawn in the same place, the number in the console is different so i dont think it is the x and y position anymore. I will give you a picture later

    #16503
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    Here is the image in the console with the debug log Debug.Log($"Spawning right pillar at: {_pillarSpawnPointRight}"); and the left

    console and here is the inspector of the water pillar at the same time at the console log water pillar inspector
    #16537
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    What should I check next? Do you have any idea yet? I could let you access to my project to checking the problem if you want

    #16567
    A_DONUT
    Level 7
    Moderator
    Helpful?
    Up
    0
    ::

    do u need any more help?

    #16578
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    Yes. The boss still making the pillar stack into 1 place only. I has tried some different way to fix it but still not work at all

    #16581
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Sorry Pepe. Yes, please share your project with us.

    #16586
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    here is the link of my project. Sorry for replying late https://drive.google.com/file/d/1LWgEOmlEMzlzOTJFGvjGJZZn2IfaXOVH/view?usp=drive_link

    #16595
    A_DONUT
    Level 7
    Moderator
    Helpful?
    Up
    1
    ::

    I’d be glad to help you with the boss attack issue in your code. Here’s a breakdown of the problems and potential solutions, incorporating insights from the ratings: Issues:

    • Rapid Punches: The TriplePunch coroutine triggers the “Punch” animation three times in rapid succession using WaitForSeconds delays. This creates the illusion of very fast punches.
    • Null Reference Error: The PunchDamagePlayer function uses CorruptedMonk.Instance to access the monk’s properties, but it might be null if the CorruptedMonk object hasn’t been instantiated yet.
    • Animator Reset: The animation triggers are reset after each punch using anim.ResetTrigger(“Punch”), which might prevent the animation from playing smoothly if you intend for it to flow continuously during the triple punch sequence. Solutions:
    • Control Punch Speed: To slow down the punches, modify the delays within the TriplePunch coroutine. Instead of WaitForSeconds, consider using yield return new WaitUntil(() => // condition to wait for). Here’s an example:
     IEnumerator TriplePunch()
    {
        attacking = true;
        rb.velocity = Vector2.zero;
    
        // Wait until a certain animation frame is reached (adjust frame number as needed)
        yield return new WaitUntil(() => anim.GetCurrentAnimatorStateInfo(0).normalizedTime >= 0.5f);
    
        anim.SetTrigger("Punch");
    
        // Wait for a set duration (adjust as needed)
        yield return new WaitForSeconds(0.3f);
    
        // Repeat for the second and third punches with appropriate delays
        // ...
    
        ResetAllAttack();
    }
    
    • Handle Null Reference: Ensure that the CorruptedMonk object exists before accessing it. You can add a check or use a safer reference approach:
      • Check for Existence:
     void PunchDamagePlayer()
    {
        if (CorruptedMonk.Instance != null)
        {
            // Access CorruptedMonk properties here
            Hit(CorruptedMonk.Instance.sideAttackTransform, CorruptedMonk.Instance.sideAttackArea);
        }
    } 
    
    • Safer Reference (if CorruptedMonk is a Singleton):
     private CorruptedMonk corruptedMonk;
    void Start()
    {
        corruptedMonk = CorruptedMonk.Instance;
    }
    
    <pre> void PunchDamagePlayer()
    {
        if (corruptedMonk != null)
        {
            Hit(corruptedMonk.sideAttackTransform, corruptedMonk.sideAttackArea);
        }
    }
    
    • Consider Animation Flow: If you want the punches to blend smoothly, keep the animation triggers active (avoid ResetTrigger) and adjust delays to control the flow. Additional Considerations:
    • Debugging: Use Debug.Log statements or the debugger to inspect values and identify where errors occur.
    • Animation Speed: The animation speed can be controlled within the animation editor if the code doesn’t directly set it.
    • State Management: If Monk_State1 triggers other behaviors, ensure they don’t interfere with the punch timing. By combining these solutions and refining your code, you should be able to achieve the desired punch speed, avoid null reference errors, and potentially create a smoother animation flow.
    has upvoted this post.
    #16596
    pepecry
    Level 7
    Participant
    Helpful?
    Up
    0
    ::

    I forgot about this. I had already handle the rapid punch. It because in the animation. I create 3 event for it with out knowing that i was make it 3. So i deleted it and the boss in no longer making ultra fast punch. The problem is only the null ref and the water pillar at 1 place. And the death sceen + resume in other topic. Glad if you could help me find out the way to fix the deathsceen and the resume issues. My project link in the above. You could check it out

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

Go to Login Page →


Advertisement below: