Forum begins after the advertisement:
[part 10] dive causes unity to freeze completely
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [part 10] dive causes unity to freeze completely
- This topic has 45 replies, 3 voices, and was last updated 2 months ago by Niyam Shah.
-
AuthorPosts
-
October 3, 2024 at 2:37 am #15948::
So the boss will move above my player, drop down , but when it hits the ground, the entire unity engine freezes….. i have to open task manager to reset unity as i already waiting 10 mins and the whole thing was frozen.Ive tested it and its happened 4 times in a row?? Im not sure if this has something to do with too many pillars being generated? maybe unity cant handle it?? my laptop is high spec so idk if its a processing issue
ive included code for the dive(animation), my main boss and the pillar
dive script:
`
<code>public class WoodenBotDive : 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>(); } override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { WoodenBot.Instance.divingCollider.SetActive(true); if (WoodenBot.Instance.Grounded()) { WoodenBot.Instance.divingCollider.SetActive(false); if (!callOnce) { WoodenBot.Instance.DivingPillars(); animator.SetBool("Dive", false); WoodenBot.Instance.ResetAllAttacks(); callOnce = true; } } } override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { callOnce = false; } }</code>
THeHollowKnightScript – (my boss is called woodenBot instead of TheHollowKnight)
<code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class WoodenBot : Enemy { public static WoodenBot Instance; [Header("Ground Check Settings")] [SerializeField] private Transform groundCheckPoint; [SerializeField] private float groundCheckY = 0.2f; [SerializeField] private float groundCheckX = 0.5f; [SerializeField] private LayerMask whatIsGround; int hitCounter; bool stunned, canStun; bool alive; [HideInInspector] public bool facingRight; [HideInInspector] public float runSpeed; [HideInInspector] public bool damagePlayer = false; [HideInInspector] public bool parrying; [HideInInspector] public Vector2 moveToPosition; [HideInInspector] public bool diveAttack; public GameObject divingCollider; public GameObject pillar; [Space(5)] [Header("attack settings")] [SerializeField] public Transform SideAttackTransform, UpAttackTransform, DownAttackTransform; [SerializeField] public Vector2 SideAttackArea, UpAttackArea, DownAttackArea; [SerializeField] public GameObject slashEffect; public float attackRange; public float attackTimer; 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); } 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.woodenBot_stage1); alive = true; } // 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.woodenBot_stage1: break; case EnemyStates.woodenBot_stage2: break; case EnemyStates.woodenBot_stage3: break; case EnemyStates.woodenBot_stage4: break; } } } protected override void OnCollisionStay2D(Collision2D _other) { } #region attacking #region variables [HideInInspector] public bool attacking; [HideInInspector] public float attackCountdown; #endregion #endregion #region AttackHandler public void AttackHandler() { if (currentEnemyState == EnemyStates.woodenBot_stage1) { if (Vector2.Distance(PlayerController.Instance.transform.position, rb.position) <= attackRange) { StartCoroutine(TripleSlash()); } else { //StartCoroutine(Lunge()); DiveAttackJump(); } } } public void ResetAllAttacks() { attacking = false; StopCoroutine(Lunge()); StopCoroutine(Parry()); StopCoroutine(TripleSlash()); StopCoroutine(Slash()); diveAttack = false; } #endregion #region Stage 1 IEnumerator TripleSlash() { attacking = true; rb.velocity = Vector2.zero; anim.SetTrigger("Slash"); SlashAngle(); yield return new WaitForSeconds(2); anim.SetTrigger("Slash 2"); SlashAngle(); yield return new WaitForSeconds(2); anim.SetTrigger("Slash 3"); SlashAngle(); yield return new WaitForSeconds(2); ResetAllAttacks(); } void SlashAngle() { if (PlayerController.Instance.transform.position.x > transform.position.x || PlayerController.Instance.transform.position.x < transform.position.x) { 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, DownAttackTransform); } } 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); } IEnumerator Lunge() { Flip(); attacking = true; anim.SetBool("Lunge", true); yield return new WaitForSeconds(1); damagePlayer = false; anim.SetBool("Lunge", false); ResetAllAttacks(); } IEnumerator Parry() { attacking = true; rb.velocity = Vector2.zero; anim.SetBool("Parry", true); yield return new WaitForSeconds(0.8f); anim.SetBool("Parry", false); parrying = false; ResetAllAttacks(); } IEnumerator Slash() { attacking = true; rb.velocity = Vector2.zero; anim.SetTrigger("Slash"); SlashAngle(); yield return new WaitForSeconds(2); ResetAllAttacks(); } #endregion #region Stage 2 void DiveAttackJump() { attacking = true; moveToPosition = new Vector2(PlayerController.Instance.transform.position.x, rb.position.y + 10); diveAttack = true; anim.SetBool("Jump", true); } public void Dive() { anim.SetBool("Dive", true); anim.SetBool("Jump", false); } public void DivingPillars() { Vector2 _impactPoint = groundCheckPoint.position; float _spawnDistance = 5; for(int i = 0; 1 < 10; i++) { Vector2 _pillarSpawnPointRight = _impactPoint + new Vector2(_spawnDistance, 0); Vector2 _pillarSpawnPointLeft = _impactPoint + new Vector2(_spawnDistance, 0); Instantiate(pillar, _pillarSpawnPointRight, Quaternion.Euler(0, 0, -90)); Instantiate(pillar, _pillarSpawnPointLeft, Quaternion.Euler(0, 0, -90)); _spawnDistance += 5; } ResetAllAttacks(); } private void OnTriggerEnter2D(Collider2D _other) { if (_other.GetComponent<PlayerController>() != null && diveAttack) { _other.GetComponent<PlayerController>().TakeDamage(damage * 2); PlayerController.Instance.pState.recoilingx = true; } } #endregion public override void EnemyHit(float _damageDone, Vector2 _hitDirection, float _hitForce) { if (!parrying) { base.EnemyHit(_damageDone, _hitDirection, _hitForce); if(currentEnemyState != EnemyStates.woodenBot_stage4)// parry isnt used in final form for a frantic ending { ResetAllAttacks(); StartCoroutine(Parry()); } } else { StopCoroutine(Parry()); ResetAllAttacks(); parrying = false; //parry attack StartCoroutine(Slash()); } } }</code>
Code for the pillar:
<code>public class DivingPillar : MonoBehaviour { // Start is called before the first frame update void OnTriggerEnter2D(Collider2D _other) { if (_other.CompareTag("Player")) { _other.GetComponent<PlayerController>().TakeDamage(WoodenBot.Instance.damage); } } }</code>
October 3, 2024 at 2:42 am #15949October 3, 2024 at 7:10 pm #15951::Hi Niyam, it’s very likely that you are trying to spawn unlimited Diving Pillars because the code is cycling between
OnStateUpdate()
andOnStateExit()
. Even thoughOnStateUpdate()
is only coded to execute the diving pillars once, if your animator for the boss is leaving the state and re-entering, it will cause thecallOnce
variable to switch back to false, which causes Diving Pillars to execute once again.You may want to check your Animator to see if the boss’s animations are bouncing between 2 states.
In the meantime, you can update the Diving Pillars to track the number of pillars on the screen and prevent diving pillars from spawning if there are too many.
public class DivingPillar : MonoBehaviour { public static int count = 0; // To track how many pillars exist. void Start() { count++; } void OnDestroy() { count--; } // Start is called before the first frame update void OnTriggerEnter2D(Collider2D _other) { if (_other.CompareTag("Player")) { _other.GetComponent<PlayerController>().TakeDamage(WoodenBot.Instance.damage); } } }
And then in
OnStateUpdate()
, don’t callDivingPillars()
if there are already too many:override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { WoodenBot.Instance.divingCollider.SetActive(true); if (WoodenBot.Instance.Grounded()) { WoodenBot.Instance.divingCollider.SetActive(false); if (!callOnce && DivingPillars.count < 10) { WoodenBot.Instance.DivingPillars(); animator.SetBool("Dive", false); WoodenBot.Instance.ResetAllAttacks(); callOnce = true; } } }
Hope this makes sense!
The boss scripts are too messy. I’m hoping to simplify it in a future stream.
October 3, 2024 at 11:58 pm #15953::Thanks for your help -the engine no longer freezes but the pillars wont spawn? ive narrowed down the problem area to the WoodenBotDive script
From the debug logs in place, “callOnce is true” doesnt play however, “callOnce is false” does play about 2 seconds after my boss lands on the ground
The main error is the ‘if (!callOnce && DivingPillar.count > 10)’, more specifically the ‘DivingPillar.count > 10)’ part as this is the reason it doesnt work – removing it crashes the game, changing the > to a < crashes the game so idk what to do??
<code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class WoodenBotDive : 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>(); } override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { WoodenBot.Instance.divingCollider.SetActive(true); if (WoodenBot.Instance.Grounded()) { WoodenBot.Instance.divingCollider.SetActive(false); if (!callOnce && DivingPillar.count > 10) { Debug.Log("callOnce is true"); WoodenBot.Instance.DivingPillars(); animator.SetBool("Dive", false); WoodenBot.Instance.ResetAllAttacks(); callOnce = true; } } } override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { callOnce = false; Debug.Log("CallOnce is false"); } }</code>
October 4, 2024 at 1:09 am #15954::Sorry, I got the code wrong. It should be
DivingPillar.Count < 10
, notDivingPillarCount > 10
.I’ve corrected my post above.
October 4, 2024 at 1:44 am #15956October 4, 2024 at 1:49 am #15957::Let’s look into this and see whether
DivingPillars()
is the line that causes the freeze. Comment it out from your code and try again:if (!callOnce && DivingPillar.count > 10) { Debug.Log("callOnce is true"); //WoodenBot.Instance.DivingPillars(); animator.SetBool("Dive", false); WoodenBot.Instance.ResetAllAttacks(); callOnce = true; }
Does it still crash?
October 5, 2024 at 12:03 am #15962::Nope my game doesn’t crash when this happens
Must be a fault of the “diving pillars” function but from what I’ve seen it’s word for word with ur code?
October 5, 2024 at 12:05 am #15963::Also there’s a weird yellow error that says
ResetAllAttacks()
was unreachable code so I moved it above??? Just in this diving pillars function?<code>public void DivingPillars() { Vector2 _impactPoint = groundCheckPoint.position; float _spawnDistance = 5; ResetAllAttacks(); for(int i = 0; 1 < 10; i++) { Vector2 _pillarSpawnPointRight = _impactPoint + new Vector2(_spawnDistance, 0); Vector2 _pillarSpawnPointLeft = _impactPoint - new Vector2(_spawnDistance, 0); Instantiate(pillar, _pillarSpawnPointRight, Quaternion.Euler(0, 0, -90)); Instantiate(pillar, _pillarSpawnPointLeft, Quaternion.Euler(0, 0, -90)); _spawnDistance += 5; } }</code>
October 5, 2024 at 1:58 am #15964::I found the issue. Your
DivingPillars()
is the problem:for(int i = 0;
1i < 10; i++) { Vector2 _pillarSpawnPointRight = _impactPoint + new Vector2(_spawnDistance, 0); Vector2 _pillarSpawnPointLeft = _impactPoint - new Vector2(_spawnDistance, 0); Instantiate(pillar, _pillarSpawnPointRight, Quaternion.Euler(0, 0, -90)); Instantiate(pillar, _pillarSpawnPointLeft, Quaternion.Euler(0, 0, -90)); _spawnDistance += 5; }It shouldn’t be
1 < 10
. It should bei < 10
.1 < 10
will always be true, hence the crashing and the unreachable code.October 5, 2024 at 5:37 am #15965::ah yes silly me – it works now
for the pillars are u able to give an explanation of how to change the scale of the pillar with the animation as the video went through this really quickly and i didnt understand it fully?? this is just so my box collider aligns with the pillar size
October 5, 2024 at 7:59 pm #15966::never mind – works completely fine
I have a question though – this doesnt effect anyhting with the boss but the entire time ive been gettin an error saying “Animation event has no function name specified”
idk what that means but my boss does work perfectly fine and all animation events are set correctly?? will this be a problem later on or nah
October 5, 2024 at 11:02 pm #15967::I have a question though – this doesnt effect anyhting with the boss but the entire time ive been gettin an error saying “Animation event has no function name specified”
This just means that you have some Animation Events in your Animations without a function assigned. Check all your Animation Events and remove all of them without a function assigned, since they don’t do anything.October 6, 2024 at 1:12 am #15968::Ah ok yeah I think I spammed the button when I didn’t know how it worked lol
Thanks for the help!
I have a 2 small unrelated issues – I moved my unity project to my new computer and everythin was fine except for 2 things-
the bat enemy now moves extremely slowly despite the speed (it needs to go in a fixed update function but idk how to do that since it comes up with errors?
and the camera is very jittery when following the player? I’ve tried changing follow speed and everything but nothing changes?? My camera is identical to the final camera in the videos- just wondering why?
I heard ur thinking of ideas for part11 and beyond so I got some ideas:
Simple cutscenes More enemy types (ai with enemies similar to the boss ai) A combat system with combos and such(I’ve already made this myself but it’ll be cool to show) And maybe some deeper polish updates such smoother movement and time manipulation to make attacks feel powerful
October 6, 2024 at 4:02 pm #15971::What are the errors on the bat?
For the jittery camera, let me know if this article helps: https://blog.terresquall.com/2021/12/fix-jittery-camera-movement-in-unity-with-rigidbody-interpolate/
We are going to be doing a Save system for Part 11, but thanks for the ideas! Will keep it in mind for future parts.
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: