Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Mono OnDestroy() need a caller property.

Discussion in '2019.3 Beta' started by Quatum1000, Aug 22, 2019.

  1. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    889
    Hi Team,

    Mono:OnDestroy() becomes more and more problematically to handle.
    There are so many callers that execute OnDestroy() so it becomes nearly impossible to react correctly on.

    Callers are eg:
    + New PrefabEditing Behavior,
    + Unity start - stop,
    + EditorApplication.wantsToQuit,
    + Scene.Load/Save/Remove, Build behavior,
    + User deletes from hierarchy,
    + Delete from any API.. SubScene, DestroyObjectImmediate, ...
    and a lot more.

    There is no chance to get any information about the caller. Several freaky workarounds work, but how long they will?
    if (Time.frameCount == 0 || EditorApplicationQuit) {
    return;
    };


    The most reason are references. For example if you want to prevent deleting prefabs from scenes that have references to other scenes, you get totally trapped without any info about the DestroyObjectImmediate() caller.

    Additionally DOTS SubScene procedural generation by script. Did you ever tried to duplicate a prefab by script? Here is the one that does:
    Code (CSharp):
    1.     public static class ValueTools {
    2.  
    3.         /// <summary>
    4.         /// Duplicate GameObject excepting Prefabs of Scene
    5.         /// </summary>
    6.         /// <param name="gobject"></param>
    7.         /// <returns></returns>
    8.         public static GameObject DuplicateGameObject(GameObject gobject) {
    9.  
    10.             // Get original Position and Rotation
    11.             var pos = gobject.transform.position;
    12.             var rot = gobject.transform.rotation;
    13.  
    14.             // Get the current Scene. Selection can be different from the active Scene
    15.             var sceneCurrent = SceneManager.GetActiveScene();
    16.  
    17.             SceneManager.SetActiveScene(gobject.scene);
    18.             GameObject goCopy = null;
    19.  
    20. #if UNITY_EDITOR
    21.  
    22.             // ? GameObject is a Prefab ?
    23.             if (PrefabUtility.GetPrefabAssetType(gobject) == PrefabAssetType.Regular) {
    24.  
    25.                 // Get the original path of the Prefab in the AssetDatabase
    26.                 var path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(gobject);
    27.  
    28.                 // Create an Object out of the AssetDatabase given path
    29.                 var obj = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject));
    30.  
    31.                 // Instantiate to the Scene where the otiginal GameObject was located
    32.                 goCopy = PrefabUtility.InstantiatePrefab(obj) as GameObject;
    33.  
    34.                 // The instantiated GameObject has default values are based on the original AssetDatabase Prefab
    35.                 // Use Reflection to get all the data from the scene Prefab to match
    36.                 // Parent GameObject
    37.                 var goAllComponents = gobject.GetComponents(typeof(Component));
    38.                 var goCopyAllComponents = goCopy.GetComponents(typeof(Component));
    39.  
    40.                 for (int idx = 0; idx < goAllComponents.Length; idx++) {
    41.                     if (typeof(MeshFilter) == goAllComponents[idx].GetType()) { }
    42.                     // Do nothing, otherwhise we leak Unity
    43.                     else {
    44.                         //.GetComponentValues comes from the namespace ComponentUtility.ValueTools
    45.                         // goCopyAllComponents[idx] = goCopyAllComponents[idx].GetComponentValues(goAllComponents[idx]);
    46.                         goCopyAllComponents[idx].GetComponentValues(goAllComponents[idx]);
    47.                         if (goCopyAllComponents[idx] == null /* default */) {
    48.                             Debug.LogError(".GetComponentValues(Component) does not matched!");
    49.                         }
    50.                     }
    51.                 }
    52.  
    53.                 // Child GameObjects
    54.                 Transform[] goChilds = new Transform[gobject.transform.childCount];
    55.                 Transform[] GoCopyChilds = new Transform[goCopy.transform.childCount];
    56.  
    57.                 for (int idx = 0; idx < goChilds.Length; idx++) {
    58.  
    59.                     goChilds[idx] = gobject.transform.GetChild(idx);
    60.                     GoCopyChilds[idx] = goCopy.transform.GetChild(idx);
    61.  
    62.                     goAllComponents = goChilds[idx].GetComponents(typeof(Component));
    63.                     goCopyAllComponents = GoCopyChilds[idx].GetComponents(typeof(Component));
    64.  
    65.                     for (int cidx = 0; cidx < goAllComponents.Length; cidx++) {
    66.                         if (typeof(MeshFilter) == goCopyAllComponents[cidx].GetType()) { }
    67.                         // Do nothing, otherwhise we leak Unity
    68.                         else {
    69.                             goCopyAllComponents[cidx].GetComponentValues(goAllComponents[cidx]);
    70.                             if (goCopyAllComponents[cidx] == null /* default */) {
    71.                                 Debug.LogError(".GetComponentValues(Component) does not match!");
    72.                             }
    73.                         }
    74.                     }
    75.                 }
    76.             } else goCopy = GameObject.Instantiate(gobject);
    77. #else
    78.             goCopy = GameObject.Instantiate(gobject);
    79. #endif
    80.  
    81.             if (gobject.transform.parent != null)
    82.                 goCopy.transform.parent = gobject.transform.parent;
    83.  
    84.             goCopy.transform.localPosition = pos;
    85.             goCopy.transform.localRotation = rot;
    86.  
    87.             goCopy.name = gobject.name;
    88.             goCopy.SetActive(true);
    89.  
    90.             SceneManager.SetActiveScene(sceneCurrent);
    91. #if UNITY_EDITOR
    92.             Selection.activeGameObject = goCopy;
    93. #endif
    94.             return goCopy;
    95.         }
    96.  
    97.         /// <summary>
    98.         /// Get all values of a Component by Reflection
    99.         /// </summary>
    100.         /// <typeparam name="T"></typeparam>
    101.         /// <param name="component"></param>
    102.         /// <param name="componentToGetFrom"></param>
    103.         /// <returns></returns>
    104.         public static T GetComponentValues<T>(this Component component, T componentToGetFrom) where T : Component {
    105.             Type type = component.GetType();
    106.             if (type != componentToGetFrom.GetType())
    107.                 return default(T);
    108.  
    109.             BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;
    110.             PropertyInfo[] propertyInfos = type.GetProperties(flags);
    111.             foreach (var propertyInfo in propertyInfos)
    112.                 if (propertyInfo.CanWrite)
    113.                     try { propertyInfo.SetValue(component, propertyInfo.GetValue(componentToGetFrom, null), null); } catch { }
    114.  
    115.             FieldInfo[] fieldinfos = type.GetFields(flags);
    116.             foreach (var fieldinfo in fieldinfos)
    117.                 fieldinfo.SetValue(component, fieldinfo.GetValue(componentToGetFrom));
    118.  
    119.             return component as T;
    120.         }
    121.     }
    122.  

    On the ability to get an info about the DestroyObject() caller:
    Code (CSharp):
    1. void OnDestroy() {
    2.     if (OnDestroy.Caller == SCENE_REMOVE || OnDestroy.Caller == UNITY_START) return;
    3. }
    It would be very efficient to have control over OnDestroy().