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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Question Issue when destroying and then instantiating the same prefab varient

Discussion in 'Scripting' started by Autoface, Aug 8, 2023.

  1. Autoface

    Autoface

    Joined:
    Sep 23, 2013
    Posts:
    112
    Hi, As the title suggests im having some issue with instantiating a prefab.... :/

    Ill provide more detail...
    Lets say my player is digging up some grass... the result will be a "dirt tile" is instantiated and the "grass tile" gets destroyed... This works fine.
    Now my player is digging up some dirt... the result should be a new "dirt tile" is instantiated and this "dirt tile" gets destroyed... BUT strange things happen when the object that is being destroyed is the same varient as the object that is being instatiated.

    So my tiles have a hp variable and when the hp variable is at 0 or less a new tile is instatiated and then this tile is destroyed. If its the same kind of prefab varient then the hp value doesn't reset. it remains at 0 on the newly instantiated object.
    It works fine if i instantiate a prefab that isn't the same... eg instantiate dirt and destroy grass...
    But it doesnt work when instantiate dirt and destroy dirt... how strange...

    Just to be clear the dirt object is not trying to instatiate a copy of its self. Its instatiating a prefab varient from the project directory so im not sure why this is happening. The prefabs im instantiating are all varients of a "master terrain prefab"... just with different values.

    Example code that is doing this operation:
    Code (CSharp):
    1.  
    2. **** somewhere up high ****
    3. [SerializeField] GameObject newTile;
    4. **** somewhere up high ****
    5.  
    6.  
    7. void SpawnAndDestroy()
    8. {
    9.  
    10.    Vector2 pos = gameObject.transform.position;
    11.    int x = (int)pos.x;
    12.    int y = (int)pos.y;
    13.  
    14.    gameObject.transform.SetParent(null);
    15.  
    16.    gameControl_Script.terrainLayer[x, y] = Instantiate(newTile, pos, Quaternion.identity);
    17.    gameControl_Script.terrainLayer[x, y].transform.SetParent(gameControl_Script.terrain.transform);
    18.  
    19.    Destroy(gameObject);
    20. }
    21.  
    I have tried implementing this a few different ways... Addings extra steps like setting the following before instantiating.
    Code (CSharp):
    1. gameControl_Script.terrainLayer[x, y] = null;
    You can see i'm clutching at straws by also setting the parent of the game object to null.

    I've also tried destroying the gameObject in the next frame by using a coroutine.
    Everytime the result is the same. If I instantiate dirt and destroy "self" which is also dirt then its as if the values from the desroyed object remain in the new instantiated object.

    If its a different prefab varient then poof it works.

    Any ideas of what could be causing this?

    Thanks
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    I'm guessing perhaps it's because your newTile is being recycled to create your replacement?

    Have you tried ALSO calling this same Instantiate block of code on the first copy and keeping newTile pristine and doing absolutely nothing to it? eg, treating it as a prefab.

    Are these just bog-standard GameObjects? Or are they Tilemap Tiles? Tiles (TileBase) are kinda weird because they're ScriptableObjects...
     
    CodeSmile likes this.
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,085
    The issue doesn‘t seem to be in the code you posted. But the usage pattern tells me it‘s likely a bug in gameControl_Script (what an odd name).

    A general best practice rule is to not expose collections that a class manages. This array assignment should be handled by gameControl_Script itself as it owns the collection. It may be doing something else that gets in the way of this array modification done by an external source, or it may not properly update its internal state.
     
  4. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    Altough always interesting to know why things don't work the way you expect, isn't it simpler to just reset the hp for that dirt tile when this scenario happens? Instead of destroying and recreating the same thing
     
  5. Autoface

    Autoface

    Joined:
    Sep 23, 2013
    Posts:
    112
    Hi all, thanks for the replys.

    What i am instantiating are GameObject prefabs. Thats all.The dirt tile isn't being recycled. Its using a direct reference to a prefab in the project directory which is why its strange.


    The GameControl_Script, I'm just using it to store the most important stuff for now and doesn't contain any actual logic, ill probably rename it to something more appropriate at a later date. In this case though it just contains a multidimentional array that contains all of my tiles (which are GameObjects). The map is procedurally generated and this is just the place that im storing the references to those instantiated GameObjects. Perhaps it's happening because I am removing the reference to the current gameobject and adding a reference to another gameobject in the same frame?? I dunno.


    I was thinking that. If its a different gameobject then I could destroy and replace. If its the same kind of gameobject then i could just reset the values. I decided against it for consistency reasons.

    I'm going to do some more experimentation to see if I can get anymore details.
     
  6. Autoface

    Autoface

    Joined:
    Sep 23, 2013
    Posts:
    112
    Ok so i've just tried removing the array out of the mix and I still get the same result.

    Code (CSharp):
    1.  
    2. GameObject tile = Instantiate(tileToSpawn, pos, Quaternion.identity);
    3.  
    4. Destroy(gameObject);
    Its only happening when spawning a prefab that is the same as the as the one that is being destroyed. What?????
     
  7. Autoface

    Autoface

    Joined:
    Sep 23, 2013
    Posts:
    112
    Ok, so I used the work around as suggested by ijmmai by just resetting the hp if the object is the same as the one to be spawned.

    It works but sad face. I still want to know what was causing the original issue.
     
    Last edited: Aug 9, 2023
  8. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    When you debug right after instantiation, for hp value, is it already 0, or does it lose its value elsewhere?
     
  9. Autoface

    Autoface

    Joined:
    Sep 23, 2013
    Posts:
    112
    So When I instantiate a dirt prefab and destroy the original dirt prefab, the hp value is already at 0, so its as if unity is using the value of the gameobject that is about to be destroyed.

    This is the current work around for now:

    Code (CSharp):
    1.  
    2. if (tileToSpawn == gameObject)
    3.         {
    4.             hp = maxHP;
    5.         }
    6.         else
    7.         {
    8.             gameControl_Script.terrainLayer[x, y] = Instantiate(tileToSpawn, pos, Quaternion.identity);
    9.             gameControl_Script.terrainLayer[x, y].transform.SetParent(gameControl_Script.terrain.transform);
    10.  
    11.             Destroy(gameObject);
    12.         }
     
    Last edited: Aug 9, 2023
  10. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Personally I would be doing this a different way. I'm not fully sure how you have it setup, but any time I see "Destroy()" being called, it makes me grind my teeth.. lol...

    Mainly because Object Pooling is a far better method, especially if you plan to change, change back, or change to another, and change back.

    But I don't think destroying or object pooling is what you want here. If you have a master/parent object of "Terrain" and it has sub-classes(grass/dirt/stone/iron ore/etc...) these should just be functionalities within said prefab. One way I handle this is:
    Code (CSharp):
    1. public enum TileType
    2. {
    3.    None,
    4.    Grass,
    5.    Dirt,
    6.    Stone,
    7. }
    8. public TileType tType;
    And then when values cause it to change, it resets it's hitCounter or hpValue, along with material and anything else it needs. Because if you think about it in it's nutshell, that piece of terrain should never change. It is already the master/parent, it's position doesn't change, and all it needs to do is just change it's type/sub-class.

    Unless of course the tile/terrain piece/area is so different(i.e. Water/Ocean), then I can see it being comparable to maybe switching out the parent class? But even still, I wouldn't be destroying it, it would be disabled and pooled for eventual other use. :)
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
  12. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    I disagree your disagree! lol...

    As far as a performance hit on Instantiation and another hit on Garbage Collection, making my own pooling setup was far less laggy(frame drop). In my cases...

    Now looking at the posts you mentioned, and thinking outside of my box, I can see where object pooling would make no sense, or show no improvements, and therefor be well out of the way(just not needed). So I will totally agree that it is on a case-by-case basis, and retract my mention of it "always being better". :)
     
    Kurt-Dekker likes this.
  13. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    Yeah, for there's a place... if you are making a bullet hell game where you REALLY are blasting a ton of shots every frame, sure... maybe. MAYBE!

    But even if you choose to do pooling, AND it miraculously gives you a performance increase, you still get to become the official owner and operator of every piece of state in that bullet.

    Hello bugs!
     
    wideeyenow_unity likes this.
  14. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    "Hello bugs" is my middle name.. :cool:

    But I always squash them.. after googling/profiling/printing of course! :D

    But as a wise YouTube video mentioned, to which made sense to me:
    "If you've never experienced the hard ways of doing things, you'll never appreciate and retain the easy way"

    I'll of course throw in a dash of salt, of always think outside the box, as sometimes you just need to make a very ugly method just to make things work. Then go back and dissect it and make it cleaner, after you verify it can be done.
     
  15. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    "Delete all code I don't need that gives me no net positive value" is my middle name.

    Yeah, it's hard when I have to scribble all that into official paperwork.
     
    wideeyenow_unity likes this.
  16. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Unite 2020 - object pooling for performance - Time Stamp: 13:16


    Unity3D College - Jason Weimann - object pooling for performance - Whole vid:


    Tarodev - deep statistics on object pooling - Whole vid:


    But yes, I totally agree, as these references also mention, if you're not dealing with large numbers of instances, there is no noticeable performance difference. Except for the whole garbage collection issues, as most stated in the Unite 2020 vid. So no garbage, possible performance if you scale up, and less chance of heap issues? One would have a ulterior motive to say otherwise. ;)

    So I wasn't far off on my statement about object pooling, it's just something that at least needs to be known, especially to new programmers.

    But back on topic, I don't think he needs to do object pooling here, as he can just change the sub class. There really should be no need for destroy/instantiate when you can just modify. :)
     
  17. Autoface

    Autoface

    Joined:
    Sep 23, 2013
    Posts:
    112
    Hi all. Thanks for the discussion on the subject. Alot to think about. I do like the idea of using an enum insteads... Atleast that way the code can be relativley consistant throughout. I'll probably get this done over the next few days.

    However I have other unrelated issues which i want to figure out first. If I need help with those ill start a new thread... I hope I dont need to thoughh.

    Thanks again guys. Always a pleasure.
     
    wideeyenow_unity likes this.