I cannot find one good working solution to handle code clean up in an editor tool for when a user deletes an object created in the scene by the editor tool.
The whole problem is of coarse the OnDestroy been called by unity when entering playmode…
I have searched the internet and every supposed solution is a complete mess of hacking playstate delegates or OnDisable in conjunction with OnDestroy and or another 6 methods with another 5 billion boolean checks and what else … yet nothing works properly.
(I am of coarse exaggerating the above , sorry I’m at wits end trying to get this working now)
My last hope was trying “isPlayingOrWillChangePlaymode” , which is totally useless because it is only ever true when existing playmode…?
Can anybody please reveal the correct combination of hacks to give me a block of execution which is only true for users destroying an object in the scene or the hierarchy and never triggered for playmode entry/exist.
Unity Devs should seriously consider exposing a proper means for editor tool creators to handle users deleting objects!
This will not work, I need to clean up some data held by the target or there are memory leaks in the scene after deleting the object. With your code the target will be null by the time OnHierarchyChanged() is called. I have tried several methods and variants of code to no avail.
I came close to almost having a hacked solution to not trigger when entering/exiting playmode but it failed again … This time I found My clean up code is called when a user loads a new scene in the editor… and there is no callback for scene changing … sigh
I think you need to describe more, and in detail what you’re doing.
You should know it’s virtually impossible to produce memory leak in Unity. If a ScriptableObject doesn’t have a owner, it get destroyed when you save the scene. If a System.Object is not referenced, the garbarge collector catches it.
technically unity does clean up as you mentioned … but it still outputs those annoying messages of objects leaks in the scene which will be output every time you click save. These messages do get cleared properly if you enter/exit play mode once after they first occur post deleting.
This happens for any dynamically created object in the editor (mesh , material etc…) which is only serialized to the scene , not serialized to disc.
Previously I was serializing to disc but it was to slow and not needed so I only create the assets in the scene but now Im left with this nonsense every time I delete an object from the scene.
This is not an unheard of issue you can google it and see many many people have been looking for some means to do proper clean up of dynamically created scene persisted objects when they get deleted by the user.
OnDestroy should be the proper handler for this but it suffers from been triggered by Unity when entering/exiting playmode as well.
All supposed methods are such a hack its not funny. Adding and removing a delegate in every OnEnable, OnDisable…
OnEnable/OnDisable are called like three times just when entering playmode alone. Then the objects get destroyed and re-created again.
So basically there is no clear means of knowing when an asset is deleted by the user. I dont even know why unity has to trigger all these methods when entering/exiting playmode or changing a scene… its of no use to any game logic or editor logic… it only serves the internal unity process
Let’s see… I do have object that are nested into one another. The solution I found to properly destroy them was fairly simple; the user can only delete an object if it was first selected.
Here’s a little class I use for any imbrication between objects;
using UnityEngine;
using System;
using System.Collections;
using System.Reflection;
/// <summary>
/// ScriptableObject are not serializable within the scope of a GameObject.
/// Therefore, they are improper to prefab, copy and so on.
/// This class' goal is to replace the ScriptableObject and still provide a polymorphic solution.
///
/// Derived from Object; no polymorphism supported.
/// Derived from ScriptableObject; cannot be prefabed, copied, duplicated or instanced.
/// Derived from MonoBehaviour; can only live on a GameObject.
/// </summary>
[HideFields]
[Serializable]
public abstract class ComponentMonoBehaviour : MonoBehaviour
{
[SerializeField]
protected MonoBehaviour owner;
public MonoBehaviour Owner
{
get { return owner; }
set { owner = value; }
}
protected virtual void Reset()
{
hideFlags = HideFlags.HideInInspector;
}
/// <summary>
/// Called when the inspector is about to destroy this one.
/// Loop in all the internal and destroy sub-components.
/// </summary>
public void OnErase()
{
FieldInfo[] infos = this.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
object value = info.GetValue(this);
if (value is ComponentMonoBehaviour)
{
ComponentMonoBehaviour component = value as ComponentMonoBehaviour;
if (component.Owner = Owner)
{
component.OnErase();
DestroyImmediate(component, true);
}
}
}
}
}
And somewhere in the 3k lines of custom Inspector, I have;
if (GUILayout.Button(Delete, style, GUILayout.MaxHeight(MIN_FIELD_HEIGHT), GUILayout.MaxWidth(MIN_FIELD_HEIGHT), GUILayout.MinWidth(MIN_FIELD_HEIGHT)))
{
if (typeof(ComponentMonoBehaviour).IsAssignableFrom(value.GetType()))
{
((ComponentMonoBehaviour)value).OnErase();
UnityEngine.Object.DestroyImmediate((UnityEngine.Object)value, true);
}
field.SetValue(null);
}
What I mean is, you can code your own Editor for some kind of base type and derive everything from it.
When an object is selected, your Editor is running. And when the user try to erase it, you destroy manually everything its referencing.
I fairly sure the only two types that can serialized within a Scene is ScriptableObject and GameObject (with MonoBehaviour). Maybe I’m wrong, but otherwise everything else must be hooked to the scene by those two types. Therefore, you only need two custom editor; one for ScriptableObject and one for MonoBehaviour.