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
- This topic has 26 replies, 3 voices, and was last updated 9 months ago by
Terence.
-
AuthorPosts
-
November 18, 2024 at 3:33 am #16384::
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); } } } }
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
November 18, 2024 at 3:53 am #16385::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
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.
November 18, 2024 at 7:15 pm #16399::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.
November 20, 2024 at 11:27 am #16449::Let’s start by fixing the NullReferenceException here:
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?
November 20, 2024 at 5:35 pm #16464::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 itAs 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(); }
November 20, 2024 at 10:41 pm #16465::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; }
November 20, 2024 at 11:03 pm #16467::In the hierachy, yes the boss spawn so much water pillar. As you can see at the below picture
And in the console, this is what it show
It up to 9 pillar and the spawn distance of 21
November 21, 2024 at 2:17 am #16469::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
November 21, 2024 at 3:14 pm #16474::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.
November 21, 2024 at 5:39 pm #16477::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
November 23, 2024 at 1:56 pm #16500::Does your console have the “Spawning left pillar at…” and “Spawning right pillar at…” messages?
November 23, 2024 at 5:06 pm #16502::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
November 23, 2024 at 6:24 pm #16503::Here is the image in the console with the debug log
Debug.Log($"Spawning right pillar at: {_pillarSpawnPointRight}");
and the leftand here is the inspector of the water pillar at the same time at the console log
November 25, 2024 at 1:34 am #16537::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
November 27, 2024 at 8:01 pm #16567November 27, 2024 at 8:35 pm #16578::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
November 27, 2024 at 9:43 pm #16581November 28, 2024 at 3:13 am #16586::here is the link of my project. Sorry for replying late https://drive.google.com/file/d/1LWgEOmlEMzlzOTJFGvjGJZZn2IfaXOVH/view?usp=drive_link
November 28, 2024 at 1:45 pm #16595::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. November 28, 2024 at 2:16 pm #16596::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
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: