Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

While loop in coroutine crashes unity

Discussion in 'Scripting' started by Jjampong, Jul 10, 2018.

  1. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    Hi, I'm following a series on youtube on 3D development, with which I created the following bit of code:

    Code (CSharp):
    1. while (attackProgress <= 1) {
    2.             if (attackProgress >= .5f && !hasDamaged) {
    3.                 hasDamaged = true;
    4.                 targetEntity.TakeDamage(damage);
    5.             }
    6.             attackProgress += Time.fixedDeltaTime * attackSpeed;
    7.             float interpolation = (-Mathf.Pow(attackProgress, 2) + attackProgress) * 4;
    8.             transform.position = Vector3.Lerp(position, attackPosition, interpolation);
    9.             yield return null;
    10.         }
    Every time the game gets into this while loop it crashes. I cannot find the reason. I hope someone could help.
     
  2. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,193
    Well, this doesn't look like it should lock up the game, since `yield return null` should wait until the next frame to run the loop again. Two things come to mind: How are you starting the coroutine? You should be doing something like `StartCoroutine(MyCoroutine());` Second: Any chance that attackSpeed is 0?
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,519
    Remove all the code inside the while loop (comment it out) except for the yield return null; statement.

    If it still crashes, your crash is somewhere else. If it stops crashing, then maybe look into the .TakeDamage() function.
     
    Bunny83 likes this.
  4. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    I tried this as well. The program still crashes. However, If I comment out the entire while loop ( meaning the declaration and entire code block ) and put yield return null on the last line of Attack(), the crash doesn't occur. So it has to be the while-loop doesn't it?
     
    Last edited: Jul 10, 2018
  5. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    Code (CSharp):
    1. [RequireComponent(typeof(CapsuleCollider))]
    2. [RequireComponent(typeof(NavMeshAgent))]
    3. public class Enemy : Entity {
    4.     [Range(0.1f, 3f)]
    5.     public float attackDistance = .5f;
    6.  
    7.     [Range(0.5f, 5f)]
    8.     public float timeBetweenAttacks = 1f;
    9.  
    10.     [Range(0.5f, 5f)]
    11.     public float attackSpeed = 3f;
    12.  
    13.     [Range(1f, 50f)]
    14.     public float damage = 10f;
    15.  
    16.     private enum State {
    17.         Idle,
    18.         Chasing,
    19.         Attacking
    20.     };
    21.  
    22.     private State currentState;
    23.     private NavMeshAgent pathfinder;
    24.     private Transform target;
    25.     private Entity targetEntity;
    26.     private Material skin;
    27.     private Color originalColor;
    28.     private float nextAttackTime;
    29.     private float collisionRadius;
    30.     private float targetCollisionRadius;
    31.     private bool hasTarget;
    32.  
    33.     protected override void Start () {
    34.         base.Start();
    35.         pathfinder = GetComponent<NavMeshAgent>();
    36.         skin = GetComponent<Renderer>().material;
    37.         originalColor = skin.color;
    38.         if (GameObject.FindGameObjectWithTag("Player") != null) {
    39.             currentState = State.Chasing;
    40.             hasTarget = true;
    41.             target = GameObject.FindGameObjectWithTag("Player").transform;
    42.             targetEntity = target.GetComponent<Entity>();
    43.             targetEntity.OnDeath += OnTargetDeath;
    44.             targetCollisionRadius = target.GetComponent<CapsuleCollider>().radius;
    45.             collisionRadius = GetComponent<CapsuleCollider>().radius;
    46.             StartCoroutine(UpdatePath());
    47.         }
    48.     }
    49.  
    50.     private void FixedUpdate () {
    51.         if (hasTarget) {
    52.             if (Time.time >= nextAttackTime) {
    53.                 float sqrDistanceToTarget = (target.position - transform.position).sqrMagnitude;
    54.                 if (sqrDistanceToTarget < Mathf.Pow(attackDistance + collisionRadius + targetCollisionRadius, 2)) {
    55.                     nextAttackTime = Time.time + timeBetweenAttacks;
    56.                     StartCoroutine(Attack());
    57.                 }
    58.             }
    59.         }
    60.     }
    61.  
    62.     private void OnTargetDeath () {
    63.         hasTarget = false;
    64.         currentState = State.Idle;
    65.     }
    66.  
    67.     IEnumerator Attack () {
    68.         currentState = State.Attacking;
    69.         pathfinder.enabled = false;
    70.         Vector3 position = transform.position;
    71.         Vector3 direction = (target.position - transform.position).normalized;
    72.         Vector3 attackPosition = target.position - direction * (collisionRadius);
    73.         float attackProgress = 0;
    74.         bool hasDamaged = false;
    75.         skin.color = Color.red;
    76.         while (attackProgress <= 1) {
    77.             if (attackProgress >= .5f && !hasDamaged) {
    78.                 hasDamaged = true;
    79.                 targetEntity.TakeDamage(damage);
    80.             }
    81.             attackProgress += Time.fixedDeltaTime * attackSpeed;
    82.             float interpolation = (-Mathf.Pow(attackProgress, 2) + attackProgress) * 4;
    83.             transform.position = Vector3.Lerp(position, attackPosition, interpolation);
    84.             yield return null;
    85.         }
    86.         skin.color = originalColor;
    87.         currentState = State.Chasing;
    88.         pathfinder.enabled = true;
    89.     }
    90.  
    91.     IEnumerator UpdatePath () {
    92.         float refreshRate = 0.2f;
    93.         while (hasTarget) {
    94.             if (currentState == State.Chasing) {
    95.                 Vector3 direction = (target.position - transform.position).normalized;
    96.                 Vector3 targetPosition = target.position - direction * (collisionRadius + targetCollisionRadius + (attackDistance / 2));
    97.                 if (!dead) {
    98.                     pathfinder.SetDestination(targetPosition);
    99.                 }
    100.                 yield return new WaitForSeconds(refreshRate);
    101.             }
    102.         }
    103.     }
    104. }
    Here is the full class. The way I see it attackSpeed can never be null, and the coroutine starts in the FixedUpdate method.
     
  6. FernandoHC

    FernandoHC

    Joined:
    Feb 6, 2018
    Posts:
    338
    I can't really thing of an obvious issue with this code, but for starting i'd check if the timescale is not zero.
    Then I would fill it lots of Debug.Logs or just debug the values on visual studio to understand what's going on.
     
  7. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    Correct me if I'm wrong but if the timescale was zero, wouldn't the game just be practically paused? In my case unity freezes up completely and has to be forced to shut down.

    This is what happens when I add the following Debug.Logs:
    Code:
    Code (CSharp):
    1. IEnumerator Attack () {
    2.         currentState = State.Attacking;
    3.         pathfinder.enabled = false;
    4.         Vector3 position = transform.position;
    5.         Vector3 direction = (target.position - transform.position).normalized;
    6.         Vector3 attackPosition = target.position - direction * (collisionRadius);
    7.         float attackProgress = 0;
    8.         bool hasDamaged = false;
    9.         skin.color = Color.red;
    10.         Debug.Log("Test 1");
    11.         while (attackProgress <= 1) {
    12.             Debug.Log("Test 2");
    13.             if (attackProgress >= .5f && !hasDamaged) {
    14.                 Debug.Log("Test 3");
    15.                 hasDamaged = true;
    16.                 targetEntity.TakeDamage(damage);
    17.             }
    18.             Debug.Log("Test 4");
    19.             attackProgress += Time.fixedDeltaTime * attackSpeed;
    20.             Debug.Log("Test 5");
    21.             float interpolation = (-Mathf.Pow(attackProgress, 2) + attackProgress) * 4;
    22.             Debug.Log("Test 6");
    23.             transform.position = Vector3.Lerp(position, attackPosition, interpolation);
    24.             Debug.Log("Test 7");
    25.             yield return null;
    26.         }
    27.         skin.color = originalColor;
    28.         currentState = State.Chasing;
    29.         pathfinder.enabled = true;
    30.     }
    Result:

    OR


    I can't seem to reproduce either result consistently and it appears to be random.
    Any more ideas?
     
  8. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    Is your attack progress ever going ti be higher 1? If not your loop is infinite forever and ever
     
  9. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    The math comes straight out of the tutorial, where it works fine. If it would ever get to be above 1 that would be consistent in both the tutorial and my code. So I don't think that is the issue.
     
    Last edited: Jul 10, 2018
  10. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    Can you remove the if statement for testing? Is it crashing too? Sorry, on mobile so cant try myself.
     
  11. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    With the entire if-statement (declaration and codeblock) commented out, unity freezes as well.
     
  12. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    So other coroutines with a while loop work? Maybe reinstall unity could be a good try
     
  13. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    In the full class I posted earlier you can also see an UpdatePath coroutine. This contains a while-loop and works fine. Unity is actually freshly installed. I Reinstalled it on this computer yesterday.
     
  14. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,732
    You need to move the yield statement out of the
    if (currentState == State.Chasing) 
    block.

    If the mob isn't 'chasing' the yield isn't ever hit, so you get a lockup.
     
  15. Jjampong

    Jjampong

    Joined:
    Jul 10, 2018
    Posts:
    9
    Yep that solved the issue. Thanks a lot
     
    Kurt-Dekker and Munchy2007 like this.
  16. Kenan_Nynor

    Kenan_Nynor

    Joined:
    Oct 28, 2022
    Posts:
    1
    I had the exact same issue but this didnt work for me. For anybody else having the same experience, this post fixed my case