Search Unity

How to prevent Coroutines from freezing Unity?

Discussion in 'Scripting' started by S3M1CZ, Dec 14, 2019.

  1. S3M1CZ

    S3M1CZ

    Joined:
    Feb 11, 2017
    Posts:
    14
    Whenever I use Coroutines there is always a risk of it completely freezing my game upon pressing play.

    For example I'm making a little game where both player and enemies have Coroutines to shoot with delay, rather than constantly. Everything works fine but when I add while() to something the game freezes upon starting it.

    Player Coroutine...
    Starts in the PlayerScript when GetMouseButtonDown(0) and ends when GetMouseButtonUp(0)

    Code (CSharp):
    1. IEnumerator FireContinuously()
    2.     {
    3.         while (true)
    4.         {
    5.             GameObject playerProjectile = Instantiate(projectile, transform.position, Quaternion.identity) as GameObject;
    6.             playerProjectile.GetComponent<Rigidbody2D>().velocity = new Vector2(direction.x * projectileSpeed, direction.y * projectileSpeed);
    7.             yield return new WaitForSeconds(firingSpeed);
    8.         }
    9.     }
    Enemy Coroutine...
    Starts in the EnemyGun Start() and runs constantly

    Code (CSharp):
    1. IEnumerator EnemyShootGun()
    2.     {
    3.         while (true)
    4.         {
    5.             yield return new WaitForSeconds(UnityEngine.Random.Range(firingSpeed, firingSpeed + firingSpeed));
    6.             GameObject enemyProjectile = Instantiate(projectile, transform.position, Quaternion.identity) as GameObject;
    7.             enemyProjectile.GetComponent<Rigidbody2D>().velocity = new Vector2(direction.x * projectileSpeed, direction.y * projectileSpeed);
    8.         }
    9.     }
    Code causing my game to freeze...
    Public method in the EnemyScript Start() that moves the enemy to certain waypoints

    Code (CSharp):
    1. public void EnterArena()
    2.     {
    3.         int targetPosition = 0;
    4.         transform.position = wayPoints[targetPosition].transform.position;
    5.  
    6.         while(targetPosition < wayPoints.Count)
    7.         {
    8.             Debug.Log("testing");
    9.         }
    10.     }
    I would really appreciate if someone knew what is going on as my other games were also plagued by Coroutines freezing after implementing while() somewhere.

    Additional info:
    Both player and enemies are made of parent object that has two children

    Parent - has script (EnemyScript / PlayerScript) controlling movement and behavior
    Child(hull) - just sprite
    Child(gun) - gun sprite with code to control its rotation and shooting (EnemyGun / PlayerGun)
     
  2. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    Don’t use while loops.
    As long as the while loop is true it will execute the code within and stop executing other code.
    Coroutines are not asynchronous. They run in steps on the main thread.

    You rather want to change your code where it involves calling the “fire” method multiple times.

    Something like
    Code (CSharp):
    1. for (var i = 0; i < N; i++)
    2. {
    3. Fire();
    4. yield return new WaitForSeconds(interval);
    5. }
    that’ll call the fire method multiple times for N amount of times with intervals in between.
     
    Last edited: Dec 15, 2019
    S3M1CZ likes this.
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    You have got to
    yield return null;
    in there. Until you exit the loop, everything is frozen.

    It actually has nothing to do with coroutines and has nothing to do with how you loop. It's the simple failure to return control to Unity so it can update the game to the next frame.
     
  4. The-Author

    The-Author

    Joined:
    Jun 29, 2018
    Posts:
    3
    To add on to Kurt’s answer and be more specific about what’s causing the infinite loop here:
    targetPosition is initialized as 0. The Count of the list will always be at least 1. Nothing in the while loop changes the value of either targetPosition or the list’s Count, so targetPosition < wayPoints.Count will always evaluate to true, which means there is no possible way to break the loop.
     
    Kurt-Dekker likes this.
  5. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    Well, sure coroutine while-loops almost always want a yield, but the code with that loop isn't a coroutine. I'm not sure what it's even for, or if the OP wanted it to be a coroutine.

    A decent pair of rules is: 1) in a coroutine, double-check that every loop has a yield -- any kind. 2) a non-coroutine doesn't need a loop -- it's being called from Update, which is already a loop.
     
    Doolittle2009 likes this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    Oh yeah, good spot, my bad.

    Perhaps OP wanted to make sure nothing else moved forward until all the waypoints had been hit, like an intro to the arena or something (just speculating)...