Search Unity

Destroy vs SetActive(false)

Discussion in 'General Discussion' started by MapuHoB, May 26, 2015.

  1. MapuHoB

    MapuHoB

    Joined:
    Aug 17, 2014
    Posts:
    50
    For the last 10h I've been doing research and experimeting with these 2, which is better for which cases, how to use on of them how to use the other one etc. I have 2 projects, both of which are totally designed to work mostly with Destroy, 2h ago I decided to change my approach to using SetActive(false) instead of Destroy as much as possible because of performance loss(smth very interesting, 2d simple gameObjects with sprites have performnce difficulties being destroyed on an android device quad 1.3 1gm memory). Here I am now on less than 1/3 of the work done realizing that I have 100+ fields keeping references of different gameObjects because of their deactivation, so that I'm able to activate them when I need them. To the end, I expect from 100 the vars to become 400+ or somewhere there, so I started getting kind of a hesitation about my approach here.
    Now I use destroy only for the Splash screen or a few other gameObjcets which are once shown in the game.

    This is my most recent question. After all the responses I got I started redesigning my structure with SetActive(false). I came up with a creative way for searching for my gameObjects in the whole game though. One main gameObject to which attached script with static Instance variable and 1 public method FindGameObject(string name)... and I search recursively for it.

    1) Is my way of searching for gameObjects a good enough way or is there some that might be more efficient?

    2) How do you use Destroy and SetActive? Which one of them you use more often and for what purposes and when?

    3) Would you recommend me to use setActive where possible instead of Destroy? If yes, is it only because of performance or there are some other pros using setActive that I don't know about?

    4) What would be an effective way of keeping track of the inactive gameObjects? My way is having private fields in some of the classes(just to mention, this way I sometimes have 5+ instances of the same gameObject, just because I access it from different scripts, this is smth I do not consider effective at all). I did some research and found another way, having some GameObject manager which keeps static instances of some of the main gameObjects in the game and I can access them from there. Another way - Singleton. What is your way? Also what way would you recommend if not your way?
     
    CringeItIn likes this.
  2. darkhog

    darkhog

    Joined:
    Dec 4, 2012
    Posts:
    2,218
    Destroy unloads object from the memory and set reference to null so in order to use it again you need to recreate it, via let's say instantiate. Meanwhile SetActive just hides the object and disables all components on it so if you need you can use it again.
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Google object pooling.
     
    R-Lindsay and Samuel411 like this.
  4. jpthek9

    jpthek9

    Joined:
    Nov 28, 2013
    Posts:
    944
    On low-memory devices or long games, you definitely don't want to clutter up the memory too much so a Destroy every now and then will help prevent a crash. For redundant objects (i.e. bullets), doing Destroy, Destroy, Destroy, New, New, New will cause a bunch of lag so you should consider using an object pooler for those objects.
     
  5. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,051
    As always it depends on the needs of the specific project. There isn't always a one size fits all solution.
     
  6. jpthek9

    jpthek9

    Joined:
    Nov 28, 2013
    Posts:
    944
    Yeah, true. I.e. Kill Many Robots for mobile never instantiated any objects at all during runtime with some clever pooling.

    @MapuHoB FindGameObject(string) isn't very fast. If you want to use it, make sure you cache the result so you don't have to call it every frame. A better method is to store your objects in a collection upon instantiating them or serialize the GO to a public variable via the inspector. For keeping track of inactive objects, just use an array or list and a collection of booleans to accomodate, which describes whether the object at a certain index is active or not. Alternatively, you could remove the object from the list and add it to another but I have a feeling this might get expensive because of all the shifting around.
     
  7. Batman_831

    Batman_831

    Joined:
    Oct 17, 2014
    Posts:
    106
    It depends on how you are using objects, if you are destroying objects like bullets and cannons using Destroy() then it will result in poor performance, so you should basically use GameObject pool, disable the object and then re-enable the object when it is needed again. but, if you are working on objects which are not be instantiated never again, like something like crate boxes which player would destroy, i think Destroy() would fit better(not sure performance wise).

    Now for your gameObject finding method...

    Generally, i don't work on mobile games so I don't have to consider much about the performance and all so i use any method which I can understand, and i usually use something like

    var objs : GameObject[] = GameObject.FindGameObjectsWithTag("TAG");

    I have read that this method has many cons, but i am not sure as i never had any problems using it( no lag spikes, no FPS drops, etc.). But, i don't call this statement every frame, i put it inside a coroutine, and check like every 2-3 secs.
     
  8. Kronnect

    Kronnect

    Joined:
    Nov 16, 2014
    Posts:
    2,905
    What I do for recurring objects during a user session (like menus or objects that will need to be recreated continuously), is to save a reference to each different type on Awake (obtained with GameObject.Find) in a Dictionary (Dictionary<name,GameObject> ). Then I have helper methods to toggle the visibility of any object, return a reference, etc. It's pretty fast and avoids memory messing.
     
    MapuHoB and Kiwasi like this.
  9. Gigiwoo

    Gigiwoo

    Joined:
    Mar 16, 2011
    Posts:
    2,981
    Try object pooling. Simplest implementation is to create a list, populate it with N initial items, and have an 'isInUse' flag. Add a 'GetNextAvailableItem' method. Add a 'MarkAsNotInUse'. You can either remove items from the list, or leave them in the list with the flag set. There's a dozen, relatively easy ways to implement object pools, and all of them are Win-Win.

    Gigi
     
  10. Kronnect

    Kronnect

    Joined:
    Nov 16, 2014
    Posts:
    2,905
    Object pooling is great but how to handle instances being destroyed along their parents? How do you keep alive these instances?
     
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    If you are going to use object pooling you need to get out of the habit of calling Destroy. In general I keep pooled objects parented to their pool.
     
    Gigiwoo and zombiegorilla like this.
  12. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    Yeah, there's a variety of strategies. The key is to plan it out in advance rather than hack it in later on.

    Some options at this stage of development:

    1. Depending on the execution order of the OnDestroy() method of MonoBehaviour, you may be able to add a component to the objects you're destroying which finds any pooled children and returns them to their pools.

    2. Another option is to make your own replacement for Destroy() which first checks for pooled children and then calls Destroy(), then just replace all calls to Destroy() with calls to that.
     
    MapuHoB, Ryiah and Kiwasi like this.
  13. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I my more heavily pooled projects I make all calls to SetActive(false). Then I have a component that picks up the OnDisable call and calls Destroy or returns to a pool as appropriate.

    But just as often I don't bother with any pooling.
     
    Gigiwoo likes this.
  14. Gigiwoo

    Gigiwoo

    Joined:
    Mar 16, 2011
    Posts:
    2,981
    Are you Indy? Use the simplest technique possible. No systems, no crazy re-usable architecture. Just a List, with some objects plus that you mark with a flag or add/remove to the list. Then, yer calling SetActive(false), which removes the need to call Destroy().

    Gigi
     
    MapuHoB and Kiwasi like this.
  15. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    #1 doesn't work as of Unity 4.3 I believe. The children are already "marked for death" and it won't allow you to re-parent them. You'll get an error in the Console if you try to do so. OnDestroy (or OnDisable) gets called first on the parent. Even on the parent's event in a script, you can't save the children.

    This caused many many lost hours trying to find a solution, but I'm convinced in the end that there just isn't one when things are auto-destroyed (like when you load a new Scene and everything that isn't persisting gets destroyed).
     
  16. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    #2 is an easy workaround for that though, right?

    Code (csharp):
    1. public static void ReturnOrDestroy(GameObject go) {
    2.     // Check hierarchy for pooled stuff. Return it.
    3.  
    4.     // Call destroy on everything else.
    5. }
     
  17. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    Kind of. That's a workaround for things that you actually call "Destroy" on. For things that get destroyed automatically by Unity - like when you load a new Scene, there's no event-based way so save them from being destroyed. I wish there was a "beforeDestroy" event for this. But there isn't.

    Example: If you had a "spawn pool" pooling prefab that is "do not destroy on load" and doesn't die between Scenes, and you wanted to put things back into it when you load a new Scene automatically when they're destroyed via a script using OnDestroy or OnDisable - that's not possible as explained in my previous post.

    So we normally use a different spawn pool prefab for each Scene because of this. Or you could create and manually call a "despawn everything in the pool" method before loading a new Scene so nothing gets lost.
     
  18. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    Ahh right, of course. I was actually discussing that in another thread recently, and suggested the exact solution you've followed up with - fresh pool per scene.

    Perhaps it'd be worth feature requesting a pre-destroy event or call. I've not personally needed one yet, but I can see how they'd be useful.
     
  19. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    I'd be glad to request it. Where are such requests made?
     
  20. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    Haha, I was going to say there's a "Request feature" option in the bug reporter, but it sends you to Unity Feedback. Since I was already there, you can vote on it here.
     
  21. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    Perfect, I'll do that :) Thank you!
     
  22. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    Apparently our request has killed the server. Attempting to log in there gets 500 Internal Server Error :(

    So I can't vote.
     
  23. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    It worked for me just then. I do get that from time to time though, with these forums too.
     
  24. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    Voted!
     
  25. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    Did you vote as well as comment? It's only showing my 3 votes. (Though to be honest I don't know if the vote count means anything.)
     
  26. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    Well I clicked on "vote" 3 times and it never changed to 4....not sure what else to do.
     
  27. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    For me I think it came up with a popup asking how many votes to spend. Can't make it come up again though. It's got a bunch of votes now in any case, so something happened.
     
  28. sluice

    sluice

    Joined:
    Jan 31, 2014
    Posts:
    416