Search Unity

Resolved Some objects were not cleaned up when closing the scene.

Discussion in 'Scripting' started by MidniteOil, May 10, 2023.

  1. MidniteOil

    MidniteOil

    Joined:
    Sep 25, 2019
    Posts:
    345
    *Edit: I've resolved this. By instantiating a new instance in the Instance getter I think I was inadvertently creating new instances when the scene was closing, probably when unsubscribing from events.

    I've refactored it to just initialize it in Awake().

    Greetings devs,

    I am getting this error in my project when I close the game:
    Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)
    The following scene GameObjects were found:
    ScoreManager


    Here is the definition of ScoreManager:
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class ScoreManager : SingletonMonoBehavior<ScoreManager>
    5. {
    6.     public event Action<int> OnScoreChanged = delegate(int score) {  };
    7.     public event Action<int> OnLivesChanged = delegate(int lives) {  };
    8.  
    9.     public int Score { get; private set; }
    10.     public int Lives { get; private set; }
    11.     public int Wave { get; private set; }
    12.  
    13.     [SerializeField] int _eggValue = 250, _extraLifeInterval = 20000;
    14.  
    15.     int _eggsCollected, _nextExtraLifeAt;
    16.  
    17.     public void ResetScore()
    18.     {
    19.         Wave = 1;
    20.         Lives = 3;
    21.         Score = 0;
    22.         _eggsCollected = 0;
    23.         _nextExtraLifeAt = _extraLifeInterval;
    24.         OnScoreChanged(Score);
    25.         OnLivesChanged(Lives);
    26.     }
    27.  
    28.     public void AddPoints(int points)
    29.     {
    30.         Score += points;
    31.         OnScoreChanged(Score);
    32.         if (Score >= _nextExtraLifeAt)
    33.         {
    34.             GainLife();
    35.         }
    36.     }
    37.  
    38.     public void ScoreEgg()
    39.     {
    40.         AddPoints(++_eggsCollected * _eggValue);
    41.     }
    42.  
    43.     void LoseLife()
    44.     {
    45.         Lives--;
    46.         OnLivesChanged(Lives);
    47.     }
    48.  
    49.     void GainLife()
    50.     {
    51.         Lives++;
    52.         _nextExtraLifeAt += _extraLifeInterval;
    53.         SoundManager.Instance.PlayFreeManSound();
    54.         OnLivesChanged(Lives);
    55.     }
    56.  
    57.     public void KillPlayer(GameObject player)
    58.     {
    59.         Destroy(player);
    60.         LoseLife();
    61.     }
    62. }
    63.  
    As you can see it derives from SingletonMonoBehavior which is defined as follows:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class SingletonMonoBehavior<T> : MonoBehaviour where T : MonoBehaviour
    4. {
    5.     static T _instance;
    6.  
    7.     public static T Instance
    8.     {
    9.         get
    10.         {
    11.             if (_instance != null) return _instance;
    12.  
    13.             _instance = FindObjectOfType<T>();
    14.             if (_instance != null) return _instance;
    15.  
    16.             var singletonObject = new GameObject(typeof(T).Name);
    17.             _instance = singletonObject.AddComponent<T>();
    18.  
    19.             return _instance;
    20.         }
    21.     }
    22.  
    23.     protected virtual void Awake()
    24.     {
    25.         if (Instance == null || Instance == this) return;
    26.         Destroy(gameObject);
    27.     }
    28. }
    I have another singleton in the project, SoundManager which follows the exact same pattern but I do not get an error for it.

    The only difference I can see is that this class has events but I am careful to unsubscribe to those events in the only class that references them:
    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3.  
    4. public class ScoreboardUI : MonoBehaviour
    5. {
    6.     [SerializeField] TMP_Text _score;
    7.     [SerializeField] Transform _playerLivesContainer;
    8.     [SerializeField] GameObject _playerLifePrefab;
    9.  
    10.     void Start()
    11.     {
    12.         ScoreManager.Instance.OnScoreChanged += OnScoreChanged;
    13.         ScoreManager.Instance.OnLivesChanged += OnLivesChanged;
    14.     }
    15.  
    16.     void OnDisable()
    17.     {
    18.         ScoreManager.Instance.OnScoreChanged -= OnScoreChanged;
    19.         ScoreManager.Instance.OnLivesChanged -= OnLivesChanged;
    20.         RemoveExtraPlayerLifeIcons(0);
    21.     }
    22.  
    23.     void OnScoreChanged(int score)
    24.     {
    25.         _score.text = score.ToString();
    26.     }
    27.  
    28.     void OnLivesChanged(int lives)
    29.     {
    30.         var childCount = RemoveExtraPlayerLifeIcons(lives);
    31.         AddMissingPlayerLifeIcons(lives, childCount);
    32.     }
    33.  
    34.     int RemoveExtraPlayerLifeIcons(int lives)
    35.     {
    36.         int childCount = _playerLivesContainer.childCount;
    37.         while (childCount > lives)
    38.         {
    39.             Destroy(_playerLivesContainer.GetChild(--childCount).gameObject);
    40.         }
    41.  
    42.         return childCount;
    43.     }
    44.  
    45.     void AddMissingPlayerLifeIcons(int lives, int childCount)
    46.     {
    47.         while (childCount < lives)
    48.         {
    49.             Instantiate(_playerLifePrefab, _playerLivesContainer);
    50.             childCount++;
    51.         }
    52.     }
    53. }
    54.  
    Here's the hierarchy where ScoreManager appears in the scene:
    upload_2023-5-10_18-23-50.png
    It seems like something is holding onto a reference to ScoreManager and keeping it from being cleaned up but darned if I can see what it is.

    Any tips for diagnosing this?

    Thank!
     
    Last edited: May 15, 2023