Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Feedback Improve "Destroy may not be called from edit mode" error

Discussion in 'Editor & General Support' started by Peter77, Jul 29, 2023.

  1. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,569
    Sometimes my game outputs the following error:
    Code (CSharp):
    1. Destroy may not be called from edit mode! Use DestroyImmediate instead.
    2. Destroying an object in edit mode destroys it permanently.
    It would be very helpful when the error message contains the name of the object on which Destroy was called.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,263
    Doubleclick on the message and it will show you the line of code.

    If that's not enough, put a breakpoint there or print the name of the object.

    Ideally editor scripts should have ZERO instances of Destroy() in them so if you find one, replace it with DestroyImmediate.
     
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,979
    This is probably my longest-lived extension method for Unity because using that you never ever need to care for what kind of Destroy you should call:
    Code (CSharp):
    1.         /// <summary>
    2.         ///     Destroys the object safely, regardless of Edit or Play mode.
    3.         ///     Depending on the mode it calls either DestroyImmediate or Destroy.
    4.         /// </summary>
    5.         /// <param name="self"></param>
    6. #if UNITY_EDITOR
    7.         public static void DestroyInAnyMode(this Object self)
    8.         {
    9.             if (Application.isPlaying == false)
    10.                 Object.DestroyImmediate(self);
    11.             else
    12.                 Object.Destroy(self);
    13.         }
    14. #else
    15.         public static void DestroyInAnyMode(this Object self) => Object.Destroy(self);
    16. #endif
     
  4. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    698
    I actually get this error in Unity Code, on player shutdown. It's aggravating because it hangs any connected debugger processes.

    That custom destroy extension is terrible, that's just asking for trouble and one day ends up accidentally deleting assets.
     
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,227
    It will never delete assets. It's not using the parameter to do so.
     
    CodeSmile likes this.
  6. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    698
    Wow thanks, I feel dumb now. Thank you for pointing that out.

    It could still damage the currently open Editor Scene in async code (e.g async void Start or async void OnDisable) when leaving playmode. (that I feel is a Unity problem - they really, really, really need to stop all game code from running when the game stops - kill -9 the CLR intepreter or threads dead, if it must be)
     
  7. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,979
    Hmmm if that is anything like OnValidate, then you may get the warning that you aren't allowed to call Destroy from this event method. I know OnEnable is one of these, so I suspect OnDisable will be the same.

    I've never used "async EventMethod" and never felt the need to do so. I wonder, why is anyone doing that over, say, running the code in a coroutine? For sure async will not make OnEnable run in parallel or in the background, or does it?
     
  8. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    698
    Probably true. I am working on a problem/bug report with async, I can test this to verify.

    async/await supports return values and exception handling.

    So with a decently coded UI dialog behaviour, one would make a Login Flow (semi-pseudocode) that could look as straightforward and linear as this:
    Code (CSharp):
    1. private async void Start => await LoginSequence();
    2.  
    3. private async Task LoginSequence() //wrapping in task makes it easier to await this from somewhere else
    4. {
    5.     while(!await EnsureServerConnection())
    6.     {
    7.         DialogResult result = await connectionRetryDialog.Execute();
    8.         if (result.status != Dialog.OK)
    9.         {
    10.             //User wants to give up.
    11.             Application.Quit();
    12.         }
    13.     }
    14.     while (!isLoggedIn)
    15.     {
    16.         //shows the login dialog, and returns the user choice and input to the application layer that deals with the server.
    17.         //dialog code is thus 100% dialog and user input code, no connection logic etc.
    18.         //and specifically, no necessary knowledge of "what comes after this dialog"
    19.         LoginDialogResult result = await loginDialog.Execute();
    20.         //Could also use the task's completed/cancelled state, or exceptions, but I prefer return value.
    21.         if (result.status == Dialog.OK)
    22.         {
    23.             try
    24.             {
    25.                 await PerformLogin(result.credentials);
    26.                 userSavegame = await LoadUserSaveGame();
    27.                 isLoggedIn = true;
    28.             }
    29.             catch (PokemonException example)
    30.             {
    31.                 //our example multiplayer library only throws exceptions, no return values, an example good for exampling ;)
    32.                 await ShowLoginErrorPopup(example);
    33.             }      
    34.         }
    35.         else
    36.         {
    37.             //User doesn't want to play and clicked CANCEL or QUIT on LoginDialog.
    38.             Application.Quit();
    39.         }
    40.         //All done, let's go to the game
    41.         await GameSceneManager.ShowTransition();
    42.  
    43.         // once transition is fully shown, load the scene (can be async if we want to keep animating or allow cancel button)
    44.         SceneManager.LoadScene(userSavegame.lastOpenGameScene); //delayed activation etc. as needed for smooth scene start..
    45.         await GameSceneManager.HideTransition();
    46.     }
    47. }
     
    Last edited: Jul 30, 2023