Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Completion of my Coroutine is freezing the editor

Discussion in 'Scripting' started by WatTheDeuce, Mar 26, 2022.

  1. WatTheDeuce

    WatTheDeuce

    Joined:
    Apr 17, 2015
    Posts:
    7
    I'm trying to use a coroutine to run a timer and perform some action when the timer reaches zero. Whenever the timer reaches zero, the entire editor freezes. I'm sure I've botched something but haven't been able to get it sorted yet, so any help is greatly appreciated.

    What's meant to happen is that a list is created and the coroutine starts a timer. When the conditions in StrikeHit() are true, the first item is removed from the list. When there are no items left in the list or when the timer expires, a new list is created. Everything seems to work as it should except for the timer. When the timer expires the editor freezes.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class RoundInstructor : MonoBehaviour
    7. {
    8.     #region Player and Audio Source
    9.     [Header("Player and Audio Source")]
    10.  
    11.     // The player character
    12.     public PlayerController player;
    13.  
    14.     // Audio source for callout voice over
    15.     public AudioSource audioSource;
    16.  
    17.     #endregion
    18.  
    19.     #region Combo Parameters
    20.     [Header("Combo Parameters")]
    21.  
    22.     // Array of available combos
    23.     public CalloutCombo[] combos;
    24.  
    25.     // The currently active combo
    26.     [SerializeField]
    27.     CalloutCombo currentCalloutCombo;
    28.  
    29.     [SerializeField]
    30.     // List of callouts remaining in current combo
    31.     List<Callout> currentComboList;
    32.  
    33.     #endregion
    34.  
    35.     #region GUI Parameters
    36.     [Header("GUI Parameters")]
    37.  
    38.     // GUI text callout label
    39.     public Text calloutText;
    40.  
    41.     // GUI score value text
    42.     public Text scoreText;
    43.  
    44.     // UI Combo Callout Text prefab
    45.     public GameObject comboCalloutTextPrefab;
    46.  
    47.     // The GUI combo list
    48.     public GameObject comboCalloutTextPanel;
    49.  
    50.     // Current callout time remaining bar
    51.     public Image calloutProgressBar;
    52.  
    53.     #endregion
    54.  
    55.     #region Scoring Parameters
    56.     [Header("Scoring Parameters")]
    57.  
    58.     // Base strike score
    59.     public int baseStrikeScoreIncrement = 25;
    60.  
    61.     // Base combo score
    62.     public int baseComboScoreIncrement = 25;
    63.  
    64.     // Combo score multiplier
    65.     int comboCount = 0;
    66.  
    67.     // Score value
    68.     float score;
    69.  
    70.     // Target strike state for current callout
    71.     RoundManager.CharacterState targetCharacterState;
    72.  
    73.     #endregion
    74.  
    75.     void Start()
    76.     {
    77.         // Set the score to 0 at the beginning of the round
    78.         score = 0;
    79.  
    80.         // Update GUI score text
    81.         scoreText.text = score.ToString();
    82.  
    83.         // Display the callout name lable
    84.         calloutText.gameObject.SetActive(true);
    85.  
    86.         // Display the callout progress bar
    87.         calloutProgressBar.gameObject.SetActive(true);
    88.  
    89.         // Create a new list for the currently active combo
    90.         currentComboList = new List<Callout>();
    91.  
    92.         // Choose the first combo
    93.         SetNextCombo();
    94.     }
    95.  
    96.     public void SetNextCombo()
    97.     {
    98.         // Stop the current combo timer
    99.         StopAllCoroutines();
    100.  
    101.         // Clear the current combo list and GUI combo list
    102.         ResetComboList();
    103.  
    104.         // Select next combo
    105.         currentCalloutCombo = combos[Random.Range(0, combos.Length)];
    106.  
    107.         // Populate the combo list and GUI combo list with each call in the current combo
    108.         foreach (Callout i in currentCalloutCombo.comboCalls)
    109.         {
    110.             // Add this callout to the list
    111.             currentComboList.Add(i);
    112.  
    113.             // Create a new GUI combo list callout element
    114.             Text labelText = Instantiate(comboCalloutTextPrefab, comboCalloutTextPanel.transform).GetComponent<Text>();
    115.  
    116.             // Set the text component of the callout element to match the current callout
    117.             labelText.text = i.calloutName;
    118.  
    119.             Debug.LogWarning(i.calloutName + " has been added to the current combo list");
    120.         }
    121.  
    122.         // Set the number of strikes included in the current combo, used for calculating the combo score modifier
    123.         comboCount = currentComboList.Count;
    124.  
    125.         // Start the combo timer
    126.         StartCoroutine(CalloutLoop());
    127.  
    128.         // Play the first callout in the current combo
    129.         PlayCallout();
    130.     }
    131.  
    132.     // Play the current callout
    133.     public void PlayCallout()
    134.     {
    135.         Debug.LogWarning("Playing next callout");
    136.  
    137.         // Check if there are any callouts remaining in the current combo list...
    138.         if (currentComboList.Count > 0)
    139.         {
    140.             Debug.LogWarning("There are currently " + currentComboList.Count + " callouts remaining in the combo");
    141.  
    142.             // Create a new callout object to store the current callout
    143.             Callout thisCall = currentComboList[0];
    144.  
    145.             // Assign the callout voice over audio clip for the current callout to the audio source
    146.             audioSource.clip = thisCall.strikeAudioCall;
    147.  
    148.             // Play the callout voice over
    149.             audioSource.Play();
    150.  
    151.             // Set the callout name label to the current callout
    152.             calloutText.text = thisCall.calloutName;
    153.  
    154.             // Set the target strike state to that of the current callout
    155.             targetCharacterState = thisCall.targetCharacterState;
    156.  
    157.             // Remove the current callout from the current combo list once it's been called
    158.             currentComboList.RemoveAt(0);
    159.  
    160.             // Remove the current callout from the GUI list of remaining callouts
    161.             Destroy(comboCalloutTextPanel.transform.GetChild(0).gameObject);
    162.         }
    163.  
    164.         // Otherwise, when there are no callouts remaining in the current combo list...
    165.         else
    166.         {
    167.             Debug.LogWarning("There are no more callouts remaining in the current combo, selecting the next combo");
    168.          
    169.             // Update score
    170.             score += baseComboScoreIncrement * comboCount;
    171.  
    172.             // Update score on GUI
    173.             scoreText.text = Mathf.RoundToInt(score).ToString();
    174.          
    175.             // Pick the next combo
    176.             SetNextCombo();
    177.         }
    178.     }
    179.  
    180.     IEnumerator CalloutLoop()
    181.     {
    182.         // Set the duration of the current combo, used to normalize the combo timer in order to set the fill amount of the combo timer progress bar
    183.         float currentComboDuration = currentCalloutCombo.comboDuration;
    184.  
    185.         // Timer for the current combo
    186.         float comboTimer = currentComboDuration;
    187.  
    188.         // While there is time remaining in the current combo timer
    189.         while (comboTimer > 0)
    190.         {
    191.             // Decrement the timer by the amount of time since the last frame
    192.             comboTimer -= Time.deltaTime;
    193.  
    194.             // Normalize the timer value to set the progress bar fill amount
    195.             calloutProgressBar.fillAmount = comboTimer / currentComboDuration;
    196.  
    197.             // Break until next frame
    198.             yield return null;
    199.         }
    200.  
    201.         Debug.LogWarning("Combo wasn't completed in time, assigning new combo");
    202.  
    203.         // Choose a new combo
    204.         SetNextCombo();
    205.     }
    206.  
    207.     public void StrikeHit()
    208.     {
    209.         // Compare call state to animation state
    210.         if (player.anim.GetCurrentAnimatorStateInfo(1).IsName(targetCharacterState.ToString()))
    211.         {
    212.             // Update score
    213.             score += baseStrikeScoreIncrement;
    214.  
    215.             // Update score on GUI
    216.             scoreText.text = Mathf.RoundToInt(score).ToString();
    217.  
    218.             // Play the next callout in the current combo list
    219.             PlayCallout();
    220.         }
    221.     }
    222.  
    223.     // Clear the current combo list
    224.     public void ResetComboList()
    225.     {
    226.         // Check if there are any callouts remaining on the GUI
    227.         while (comboCalloutTextPanel.transform.childCount > 0)
    228.         {
    229.             // While there are, remove them
    230.             Destroy(comboCalloutTextPanel.transform.GetChild(0).gameObject);
    231.         }
    232.  
    233.         // Check if there is a current combo list
    234.         if (currentComboList != null)
    235.         {
    236.             // If there is, check if there are any callouts remaining in the list
    237.             while (currentComboList.Count > 0)
    238.             {
    239.                 Debug.LogWarning("Removing " + currentComboList[0].calloutName + " from the current callout list");
    240.  
    241.                 // While there are, remove them
    242.                 currentComboList.RemoveAt(0);
    243.             }
    244.         }
    245.     }
    246. }
     
  2. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
    Code (CSharp):
    1.         // Check if there are any callouts remaining on the GUI
    2.         while (comboCalloutTextPanel.transform.childCount > 0)
    3.         {
    4.             // While there are, remove them
    5.             Destroy(comboCalloutTextPanel.transform.GetChild(0).gameObject);
    6.         }
    I just happened to see it, shouldn't really need to come to the forums for infinite loops. Destroy doesn't destroy the object immediately, it just flags it to be destroyed later.

    If your editor freezes, it's a pretty good sign that you have an infinite loop. Next time you encounter it, just put a Debug in every single loop you think might be causing the problem, and that will tell you where it is.
     
  3. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    Just to add to this - one of the solutions could be to switch your loop to a for loop and destroy the child at each index, a pretty simple switch.
     
  4. WatTheDeuce

    WatTheDeuce

    Joined:
    Apr 17, 2015
    Posts:
    7
    Thanks for the suggestion.