I'm trying to access a value of a local variable inside a lambda according to this post, but I get this excepton:
MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.
Here is my code:
private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component
{
#if UNITY_EDITOR // EditorApplication is only available when running in the editor
if (EditorApplication.isPlaying) return;
if (transform == null) return; // in case inspector field is not assigned
var t = transform;
EditorApplication.delayCall = () =>
{
var component = t.GetComponent<T>();
if (component == null)
{
LogAddingComponent<T>(t);
var addedComponent = t.gameObject.AddComponent<T>();
validationMethod.DynamicInvoke(args.Prepend(addedComponent));
}
else
{
validationMethod.DynamicInvoke(args.Prepend(component));
}
};
#endif
}
What am I doing wrong?
CodePudding user response:
You are attempting to pin transform via the variable t only to use it later during your delayCall callback. That's dangerous considering many types in Unity contain unmanaged resources hence the error. The effect is the same if you created a Font in WinForms, Disposed it, then attempted to use the font.
OP:
Is there a better way to approach this then?
From what I understand, Transform
s are always attached to the GameObject
and it is the GameObject
that is being destroyed taking the Transform
along with it.
The only way I know to prevent game object destruction is viaDontDestroyOnLoad
but I'm not sure if its applicable here since the latter is really designed for protection whilst transitioning to a new scene, but you never know perhaps it prevents things from being destroyed in other scenarios.
Try calling DontDestroyOnLoad
at the top of ValidateComponent
like so:
private static void ValidateComponent<T>(Transform transform,
Delegate validationMethod,
object[] args)
where T : Component
{
#if UNITY_EDITOR // EditorApplication is only available when running in the editor
if (EditorApplication.isPlaying) return;
if (transform == null) return; // in case inspector field is not assigned
DontDestroyOnLoad (transform.gameObject); // <---- new
var t = transform;
.
.
.
Otherwise, there is this Unity Answer, that suggests simply disabling and re-enabling the object rather than whatever is destroying the object in the first place. Following that approach you shouldn't require any changes to your code above.
Editor-time-only scripts
BTW, regarding the #if UNITY_EDITOR
bit, if you want to avoid having to decorate your code with #if
s everywhere (as an ex c/c programmer I certainly don't miss having to do that all the time), consider simply moving your script into a child folder called Editor. Unity automatically treats anything placed in such folders as Editor-time-only and won't be incorporated into your final game build. Better yet, no need for the #if
stuff.
The best reason for it though is to prevent build errors when Unity is producing your final game due to forgetting to either use #if
or Editor folders. I typically run into this one with 3rd party scripts obtained from the Unity store. Instead of having to fix the code I simply move it to a child Editor folder.
See also
- Special folder names, Unity