Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Destroy Component not always working

Discussion in 'Scripting' started by AnalogUniverse, Nov 29, 2018.

  1. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    63
    Hi All

    Ive created a custom button script which deletes and recreates a polygon collider 2d on Validate, to change the collider to match my sprite as there is no recalculate collider c# command that I can find. This works most of the time, for example when I change my button sprite property in the inspector, but sometimes Im not sure when or why it happens but when I check my button it has two colliders assigned in the inspector, Also when I try and create a prefab of my button I find there is suddenly 3 or more colliders attached. Heres the code Im using Any help greatly appreciated.

    Code (CSharp):
    1.     protected virtual void OnValidate()
    2.     {
    3.         DestroyFromEditor(gameObject.GetComponent<PolygonCollider2D>());
    4.  
    5.  
    6.         renderer = GetComponent<SpriteRenderer>();
    7.         renderer.sprite = idleImage;
    8.         gameObject.AddComponent<PolygonCollider2D>();
    9.     }
    10.  
    11.  
    12.     void DestroyFromEditor(Component obj)
    13.     {
    14.         EditorApplication.delayCall += () => DestroyImmediate(obj);
    15.     }
    Thanks
     
  2. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    63
    Does anyone know why the component doesn't always delete on validate ?
     
  3. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    Not entirely sure on this but I suspect that Calling 'DestroyImmediate' in a delayed fashion like that may be causing the inspector to revalidate due to forced update on the inspector when the component is removed. It's probably a race condition of some kind. I'm honestly surprised it's not an infinite loop, really!

    Is there a reason you are performing the component destruction using a delayed callback rather than just simply... destroying it immediately?

    Also - my inner programmer-demons are triggering so I can't help but mention the lack of a removal of your callback from the editor event even though I assume you simply omitted the code for the sake of clarity. ;)
     
  4. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    63
    After weeks of searching I just found on several forums that this was the was to delete GameObjects and components in the onValidate method, to update the GameObject in the editor, as just calling destroyImmediately on its own does nothing ! Not sure what you mean by lack of removal of the call back from the editor event, as this wasn't mentioned on any of the forums which suggest this work around, can you explain ?
     
  5. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    63
  6. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    Yikes! Okay so the example you copied from that post wasn't actually meant to be used (at least, I *hope* the person didn't actually intend it to be used). It was more of demonstration in the simplest terms of what someone would need to do.

    The event you are subscribing to is having a new anonymous method added to its list every single time that script is visible in the inspector and OnValidate() gets called (which can be very frequent) and these methods are never being unsubscribed from the event. This will cause two issues:
    1) You are actually triggering the handler multiple times. One additional time for each validation that occurs, in fact. That can add up to hundreds, thousands, or even more of repeated calls to the same handler very quickly! I dunno that this is the cause of the issues you mentioned but it certainly isn't going to help.
    2) You are leaking memory. These anonymous delegates are never going to be cleaned up until EditorApplication is. And considering this is a static event handler that will likely not happen until Unity is shutdown.

    You should instead, be storing a reference to your handler in a delegate and then unsubscribing that delegate from within the handler itself.
     
  7. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    Very interesting indeed. This seems somewhat familiar and I sort of recall reading this a while back when I had a similar issue. Can't really remember if I used this solution or did something else in the end though.
     
  8. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    63
    How do you unsubscribe from the even whats the code ?
     
  9. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    Off the top of my head you could try something like this. Define a static field to store the temporary callback lambda and then use that field to remove it within the handler itself. So something like this:

    Code (CSharp):
    1. static UnityEditor.EditorApplication.CallbackFunction DestroyCallback;
    2.  
    3. void Somefunction()
    4. {
    5. //assign the anonymous function to a delegate so that we can unsubscribe it later.
    6. DestroyCallback = () =>
    7.                 {
    8.                     GameObject.DestroyImmediate(obj);
    9.                     //unsubscribe this handler within the handler itself.
    10.                     UnityEditor.EditorApplication.delayCall -= DestroyCallback;
    11.                 };
    12.                 UnityEditor.EditorApplication.delayCall += DestroyCallback;
    13. }
    Mind you, I haven't tested this so there might be some bugs. There is the risk that if you have multiple successive calls
    to 'SomeFunction' before the delayed callback is ever triggered then you will still be leaking memory due to the fact that the value assigned to DestroyCallback will be overwritten and any previous callbacks that were subscribed will never get the chance to run and thus unsubscribe.
     
    AnalogUniverse likes this.
  10. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    63
    Thanks sluggy I think I'll have to look into other options, I'm looking at [ExecuteInEditMode] at the moment. Unity is very frustrating, I can believe there isn't a clear clean solution to updating changes made in Edit mode in the Editor, if I cant find a solution I think I'll have to look for another framework, Unity is just too hacky and frustrating to use, for me at this point, every solution Ive found has had fundamental problems and Im not super confident that Unity wont being doing all sorts of things behind my back, and crash my game somewhere down the line.

    Thanks Again !
     
  11. username123445

    username123445

    Joined:
    Dec 1, 2018
    Posts:
    6
    You should try puttting everything you need to do on the Start function and in the ends disable the script in the last line.
    In the script ,to be usure nothing goes wrong you should give default values in the declaration of the varibles
    If this doesnt work somehow maybe it exist a function that resets the component like in the editor