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:
**** somewhere up high ****
[SerializeField] GameObject newTile;
**** somewhere up high ****
void SpawnAndDestroy()
{
Vector2 pos = gameObject.transform.position;
int x = (int)pos.x;
int y = (int)pos.y;
gameObject.transform.SetParent(null);
gameControl_Script.terrainLayer[x, y] = Instantiate(newTile, pos, Quaternion.identity);
gameControl_Script.terrainLayer[x, y].transform.SetParent(gameControl_Script.terrain.transform);
Destroy(gameObject);
}
I have tried implementing this a few different ways… Addings extra steps like setting the following before instantiating.
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.
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…
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.
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
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.
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.
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:
public enum TileType
{
None,
Grass,
Dirt,
Stone,
}
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.
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”.
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.
But I always squash them… after googling/profiling/printing of course!
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.
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.
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.