Search Unity

Destroying ScriptableObjects in scene

Discussion in 'Scripting' started by CDF, Nov 21, 2014.

  1. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    So I have a scene object which references some ScriptableObjects, these ScriptableObjects cannot be saved assets, they need to reference other scene objects. And they need to be ScriptableObjects so I can use PropertyField GUI's

    Deleting the scene object will cause nested ScriptableObjects to leak. This can be solved by creating a custom editor for your scene object and listening to the OnDestroy method, going over fields using Reflection and Destroying the nested ScriptableObjects. This works ok, but Undoing this delete ooperation does not restore the ScriptableObjects that were destroyed through Reflection, sigh.

    Is there anyway to Record a delete on a ScriptableObject and upon undoing this, restore its reference in the scene object?

    I've literally changed my entire object handling code at least 15 times in the last 2 weeks because of this ScriptableObject nonsense, getting pretty tired of it.
     
  2. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    ok, found this: Undo.DestroyObjectImmediate

    but, when redoing, unity crashes :(
     
  3. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    ok, so the crash was related to OnDestroy being called when calling a redo! doesn't make any sense to me, but whatever.

    solution is to listen to UndoRedo and either enable/disable using Undo.DestroyObjectImmediate:

    Code (CSharp):
    1.  
    2. public class MyEditor : UnityEditor.Editor {
    3.    
    4.     private bool canDestroy;
    5.  
    6.     void OnEnable() {
    7.  
    8.         canDestroy = true;
    9.  
    10.         Undo.undoRedoPerformed -= OnUndoRedoPerformed;
    11.         Undo.undoRedoPerformed += OnUndoRedoPerformed;
    12.     }
    13.  
    14.     private void OnUndoRedoPerformed() {
    15.  
    16.         canDestroy = false;
    17.     }
    18.  
    19.     void OnDestroy() {
    20.    
    21.         Undo.undoRedoPerformed -= OnUndoRedoPerformed;          
    22.  
    23.         if (Application.isEditor && target == null) {      
    24.  
    25.             //destroy
    26.  
    27.             if (canDestroy) {
    28.  
    29.                 //perform nested destroy code here using Undo.DestroyObjectImmediate()
    30.                 canDestroy = false;
    31.             }
    32.         }
    33.     }
    34. }
    35.  
    why must ScriptableObjects be so difficult to work with? Can't we have a different type, like a ScriptableObjectInstance?
     
  4. RSG

    RSG

    Joined:
    Feb 20, 2013
    Posts:
    93
    When you destroy the game object, you could try something similar to this:

    Code (CSharp):
    1. // Register an undo operation on the game object that contains the scriptable object
    2. Undo.RecordObjects( holder, "Removed");
    3.  
    4. // Register undo actions on each scriptable object and remove them one at a time
    5. while(target.ScriptableObjects.Count > 0)
    6. {
    7.     // Register undo
    8.     var scriptableObject = target.ScriptableObjects[0];
    9.     Undo.RecordObjects( scriptableObject, "Removed");
    10.  
    11.     // Remove from game object
    12.     holder.ScriptableObjects.Remove(itemToRemove);
    13.  
    14.     // Destroy but register undo
    15.     Undo.DestroyObjectImmediate(itemToRemove);
    16. }
    17.  
    18. // Collapse all undo actions
    19. Undo.RecordObjects( holder, "Removed");
    20. Undo.CollapseUndoOperations(Undo.GetCurrentGroup());