In legacy code, I need to check what caused the game object to be destroyed, disabled, etc.
Is there any way to trace or to place a breakpoint to check the call stack for why the game object became disabled?
There is no native way to do this.
What you can do instead is create some extension methods something like this:
public static class DebuggingAids {
public static HashSet<UnityEngine.Object> watchList = new HashSet<UnityEngine.Object>();
public static void CheckedDestroy(this UnityEngine.Object target) {
if (watchList.Contains(target) {
// Place your breakpoint or debugging code here.
}
UnityEngine.Object.Destroy(target);
}
public static void CheckedSetActive(this GameObject target, bool active) {
if (watchList.Contains(target) {
// Place your breakpoint or debugging code here.
}
target.SetActive(active);
}
public static void CheckedSetEnabled(this Behaviour target, bool enabled) {
if (watchList.Contains(target) {
// Place your breakpoint or debugging code here.
}
target.enabled = enabled;
}
}
Now search your codebase for any instance of the following patterns, and replace them:
Destroy(foo)
--> foo.CheckedDestroy()
foo.SetActive(bar)
--> foo.CheckedSetActive(bar)
foo.enabled = bar
--> foo.CheckedSetEnabled(bar)
After you've done this, the static class above should be the only place using Destroy
/SetActive
/.enabled
directly. Everything else will go through it as an intermediate.
By adding an object you care about to the watchList
, DebuggingAids.watchList.Add(foo)
, you can trap anything that tries to disable or destroy the object from script.
This won't catch disabling/destruction that happens on an assembly reload, scene load, or exiting Play Mode, but those should be fairly obvious to detect on their own.
You can use assertions or prepocessor directives to make it so your debugging code only runs in editor, so that these indirections compile away to nothing in a release build.