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

DontDestroyOnLoad Leading to Double Objects

Discussion in 'Scripting' started by liquidgraph, Aug 27, 2010.

  1. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    So I have a manager object that controls my whole application. It exists in my starting level and I have it set to "DontDestroyOnLoad".

    Then I load other levels and my manager carries over just fine, but when the player clicks the back button to go back to the main screen (level0), I get a second instance of my manager because it existed originally in that level. Obviously this causes huge problems.

    I thought DontDestroyOnLoad would be smart enough to not replicate the object in question on every level load. Apparently it isn't. What can I do to prevent the controller from getting duplicated? I need it to exist in the first scene to begin with.

    Seems like a chicken and egg problem: I need an object to bootstrap the application, but then I don't need it when reloading the scene.
     
    Durium and bdeschryver like this.
  2. laurie

    laurie

    Joined:
    Aug 31, 2009
    Posts:
    638
    DontDestroyOnLoad() does just that: prevents the object from being destroyed. It doesn't have any effect on when the object gets created.

    One solution is to have a 'bootstrap' level that sets up all such objects and then loads the first real level. You can go back to that level, but you never go back to the bootstrap level.

    Alternatively, use a singleton class not attached to a game object. Since it's not attached to a game object, it's not tied to the game object life-cycle and will not be affected by loading/unloading levels.
     
  3. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    I need static variables in my object and exposed variables for prefabs and such. This means I can't use a singleton, right? How can I make a singleton but have it start off the scene? Is that possible?

    The bootstrap level will probably work, but seems kludgy -- extra stuff to load on start. I'm targetting the iPhone and want to keep it as efficient as possible.
     
  4. AkilaeTribe

    AkilaeTribe

    Joined:
    Jul 4, 2010
    Posts:
    1,149
    Code (csharp):
    1. // CLASS VARIABLES
    2. static var instance : GameSettings;
    3.  
    4. // EVENTS
    5. function Awake ()
    6. {
    7.     if (instance)
    8.     {
    9.         Destroy (gameObject);
    10.     }
    11.     else
    12.     {
    13.         instance = this;
    14.         DontDestroyOnLoad (gameObject);
    15.     }
    16. }
    Comes from a script called GameSettings. Upon Awake, it checks for the content of instance variable (a global variable). If there is nothing, the script put himself in the variable. Future triggering will result in autodestruction, since the place is taken.
     
  5. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    With that code, which object will get destroyed? The one the game was started with, or the second one that gets loaded upon revisiting the first scene?

    I want my original object to stay intact and not be destroyed because it has variables that need to be preserved. I'm not seeing how that works with this code.

    Awake would be called on both objects once the scene is loaded, so wouldn't they both destroy themselves?!
     
  6. JRavey

    JRavey

    Joined:
    May 12, 2009
    Posts:
    2,377
    If it is something that should persist across every level, you should not really have it in the scene.

    You would have something that instantiates the object if one is not available instead. In C#, I do this.

    Code (csharp):
    1.  
    2.     void Awake () {
    3.         if( FindObjectOfType(typeof(GameStateManager)) == null )
    4.         {
    5.             GameObject newGSM = new GameObject();
    6.             newGSM.name = "GSM";
    7.             newGSM.AddComponent<GameStateManager>();
    8.             GSM = (GameStateManager)newGSM.GetComponent("GameStateManager");
    9.         }
    10.         else
    11.         {
    12.             GSM = (GameStateManager)FindObjectOfType(typeof(GameStateManager));
    13.         }
    14. }
    15.  
    That code has has the camera which drives the menu system link into an existing GameStateManager object if it exists. If it does not exist, such as when the game is first started, it will create it. None of the gameplay scenes create a GameStateManager, because it should already exist.

    This shows a lack of understanding about what scenes are and aren't. Scenes are simply collections of objects. If the scene has the object, it will load the object. As for it being smart enough or not, it is doing exactly what it is supposed to do.
     
  7. Ntero

    Ntero

    Joined:
    Apr 29, 2010
    Posts:
    1,436
    [mod edit: removed old/broken link]

    Is the simplest functional example of a persistent object that persists from the first time you call it to game quit.

    It won't destroy any new instances.
     
    Last edited by a moderator: Apr 3, 2020
  8. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Thanks for the help guys. I ended up creating a Bootstrapper class that instantiates my game manager using JRavey's code.

    Work's fairly well, although I got one strange null reference exception error that forced me to change a Start() event to an Awake() event to fix. There was a weird problem where one of my array's wasn't being initialized fast enough on Start in one class, but it was being called from another. I'm not sure why instantiating the Manager would cause this problem as nothing else changed. The component scripts attached to the manager remained all the same.
     
  9. JRavey

    JRavey

    Joined:
    May 12, 2009
    Posts:
    2,377
    I think some of that casting is redundant, I'll double check when I get home. I just kind of threw it together one day and never looked back.
     
  10. Packetstorm

    Packetstorm

    Joined:
    Jan 6, 2013
    Posts:
    3
    fixed this issue with state code and a bootstrap level thx Laurie nobody else had the right answer
     
  11. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    I have a similar issue with a Java script timer that I am trying to get to play across scene changes from my main screen when I go from that to another scene, it works for the most part but when I return to the main scene my CountdownTimeManager object has been duplicated. I also get an error telling me that it's trying to access another objet that has been destroyed, that part I figure is because my timer text is part of a UI canvas panel and gets destroyed every time the scene is exited. My timer script does sound it's alarm if in another scene but like I say when I come back to the main scene the game object with the script is duplicated every time I return to that scene and I get that error message:

    NullReferenceException: Object reference not set to an instance of an object
    CountdownTimerManager.CurrentTime () (at Assets/CountdownTimerManager.js:47)
    CountdownTimerManager.Update () (at Assets/CountdownTimerManager.js:38)


    I'll post my script, it's java unfortunately, I wish it was C# because that's what my code guy prefers to work with and I'm not super great with scripts at the best of times. I can figure things out if they are explained really well as I edited this scripts from cobbled together bits and pieces, it works just not 100% the way I would like, any help would be appreciated, a conversion to C# would be awesome as I find WAY more information on the don't destroy on load stuff in C# ;) Anyways here is my script if anyone can lend a hand. I have my code guy hard at work on our audio recording stuff so he is pretty busy knocking himself out with that... I'm just the lowly artist doing what I can to save him a few headaches lol ;)

    Code (JavaScript):
    1. #pragma strict
    2. var Alarm : AudioClip ;
    3. var timer: float = 3600;
    4. var isFinishedLevel : boolean = true;
    5. public var displayText : UnityEngine.UI.Text;
    6. public var timeText : UnityEngine.UI.Text;
    7. var minsDisplay : String;
    8. var secsDisplay : String;
    9. var mySeconds : int = 0;
    10. private var oldTimer : float;
    11. //Begin New
    12. //End New
    13. function Awake () {
    14.          DontDestroyOnLoad (transform.gameObject);
    15.      }
    16. function Start(){
    17.    
    18.      oldTimer = timer;
    19. }
    20. function Update(){
    21.  
    22.      if (!isFinishedLevel) {
    23.          timer -= Time.deltaTime;
    24.      }
    25.    
    26.      CurrentTime();
    27. }
    28. function CurrentTime() {
    29.      var dt : System.DateTime = System.DateTime.Now;
    30.      var h : int = dt.Hour;
    31.      var m : int = dt.Minute;
    32.      var s : int = dt.Second;
    33.      timeText.text = h + ":" + m + ":" + s;
    34.    
    35.      if(mySeconds != s)
    36.      {
    37.          mySeconds = s;
    38.          Timing();
    39.      }
    40.    
    41. }
    42. function Timing()
    43. {
    44.      if (timer > 0) {
    45.          //var minsDisplay : String = parseInt( timer / 60 ).ToString();
    46.          minsDisplay = parseInt( timer / 60 ).ToString();
    47.        
    48.          //var secsDisplay : String = parseInt( timer ).ToString();
    49.          secsDisplay = parseInt( timer ).ToString();
    50.          
    51.          if ( (timer - ( parseInt(minsDisplay) * 60)) > 10 ) {
    52.               secsDisplay = parseInt( timer - ( parseInt(minsDisplay) * 60) ).ToString();
    53.          }
    54.          else {
    55.              secsDisplay = "0" + parseInt( timer - ( parseInt(minsDisplay) * 60) ).ToString();
    56.          }
    57.        
    58.          //displayText.text = minsDisplay + " : " + secsDisplay;
    59.      }
    60.      //Timer Reaches End We Can Do Something Here
    61.      else {
    62.           timer += oldTimer;
    63.           audio.PlayOneShot(Alarm);//Plays Alarm Sound
    64.           isFinishedLevel = true;//Sets Inspector Value to true or false based on what is set here
    65.           yield WaitForSeconds (0.8);//Wait Time Setting
    66.           //Do Something if Desired
    67.          
    68.           Debug.Log ("Timer Ended");
    69.      }
    70.      displayText.text = minsDisplay + " : " + secsDisplay;
    71. }
    72. //Timer Stop Button
    73. public function GoTimerStop()
    74. {
    75.      isFinishedLevel = true;
    76. }
    77. //Timer Start Button
    78. public function GoTimerStart()
    79. {
    80.      isFinishedLevel = false;
    81. }
    82. //Timer Settings
    83. public function GoTimerSetting60Sec()
    84. {
    85.      timer = 60;
    86. }
    87. public function GoTimerSetting5Min()
    88. {
    89.      timer = 300;
    90. }
    91. public function GoTimerSetting10Min()
    92. {
    93.      timer = 600;
    94. }
    95. public function GoTimerSetting20Min()
    96. {
    97.      timer = 1200;
    98. }
    99. public function GoTimerSetting30Min()
    100. {
    101.      timer = 1800;
    102. }
    103. public function GoTimerSetting40Min()
    104. {
    105.      timer = 2400;
    106. }
    107. public function GoTimerSetting50Min()
    108. {
    109.      timer = 3000;
    110. }
    111. public function GoTimerSetting1Hr()
    112. {
    113.      timer = 3600;
    114. }
    115. public function GoTimerSetting1Point5Hr()
    116. {
    117.      timer = 5400;
    118. }
    119. public function GoTimerSetting2Hr()
    120. {
    121.      timer = 7200;
    122. }
    123. public function GoTimerSetting2Point5Hr()
    124. {
    125.      timer = 9000;
    126. }
    127. public function GoTimerSetting3Hr()
    128. {
    129.      timer = 10800;
    130. }
     
  12. KhalilSanghi

    KhalilSanghi

    Joined:
    Jun 17, 2016
    Posts:
    1
    static public bool once_call;
    if (!once_call) {
    DontDestroyOnLoad (this);
    once_call = true;
    } else {
    Destroy (gameObject);
    }
     
  13. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    What is this?
    For future reference for people could you explain what this is.
    Thanks.
    (Also please use Code Tags)
    http://forum.unity3d.com/threads/using-code-tags-properly.143875/
     
  14. TheSaintOne

    TheSaintOne

    Joined:
    Feb 24, 2019
    Posts:
    1
    Thanks, great working solution!
     
  15. karthikMurugan

    karthikMurugan

    Joined:
    Mar 26, 2020
    Posts:
    1
    Simply check how many game object exists in the scene with the same tag, and destroy the original one if count greater than 1. Given code below for your reference,[Note: use this code in Awake method]
    Code (CSharp):
    1. if(GameObject.FindGameObjectsWithTag(this.gameObject.tag).Length > 1){
    2.             Destroy(this.gameObject);
    3.         }
     
  16. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,517
    While we're digging up 11-year-old Javascript posts in the year 2021, five years after Unity stopped supporting Javascript, this is a more-robust approach to ensuring you only have one instance of a given class:

    Simple Singleton (UnitySingleton):

    Some super-simple Singleton examples to take and modify:

    Simple Unity3D Singleton (no predefined data):

    https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

    Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

    https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

    These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

    If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

    Code (csharp):
    1. public void DestroyThyself()
    2. {
    3.    Destroy(gameObject);
    4.    Instance = null;    // because destroy doesn't happen until end of frame
    5. }