- Home /
Copying material destroys prefab
I'm working on a 3d tile based game. I've reached a point where i need to replace one tile for another. The tile i'm replacing is an instanced prefab (GameObject), and the way i'm going about it is simply trying to replace it's material with the material of the prefab of the tile i want it to turn into.
In short, i'm replacing the material of a game object which is a instanced prefab.
First i've tried this:
gameObject.renderer.material = prefab.renderer.material;
The first time i run the game this works well. After i quit, my prefab looses it s material component for some reason. The result are very unexpected to me:
The magenta colored squares are the tiles i was trying to replace. What is most surprising is that this is caused by permanent damage to the prefab i have in the editor... The material it had simply disappeared.
I tried a couple of different ways, and the one that worked was:
gameObject.renderer.material = Resources.Load<Material>("Materials/Dirt");
However this is not optimal, since i will not know the path of the materials... i need to be able to get them from the prefab. Also, this is done run time and i don t think the Load method has acceptable performance.
Can you explain to me why my prefab looses it's material component? Also, i would like some input on what is the best / correct way of replacing a material of a prefab instanced with a material from another prefab.
Answer by Owen-Reynolds · Jun 09, 2014 at 04:18 PM
The problem comes from just looking at the prefab's material: prefab.renderer.material
. When you touch renderer.material
for the first time, Unity automatically copies it, and assigns the copy to that renderer. If you've seen "smart" pointers to invariants, it's the same idea. All boxes share a red material: setting box6.material to blue requires that we split off a copy for box6.
So, during play it modifies the editor copy of the prefab. Once the game stops, the copied material is destroyed, as usual, and the prefab's Material link is now null. (So, in theory, the problem might not happen in a build.)
The only solution I've found is never to do that. Either to provide a separate public prefabMat
var. Or to pre-spawn the prefab, and read materials from there.
I'm not aware of anything else that works like this. Probably safe to examine all of a prefab's children, collider sizes, script values, meshes... . Just never the materials.
Why does just reading a material cause a new copy to be made? Well, Material m1 = A.renderer.material;
could be followed by m1.color=
, when it would be too late to make a copy.
Thanks a lot! Great answer!
I still require to retrieve materials and meshes, probably scripts at this point in runtime, based on an enum that describes the prefab i want to use.
I could either introduce some factories, that return these components... Or have some instances of the game objects which i can copy things from.
Do these instances need to be in the game scene? I think that would be ok if i can disable their rendering and all the scripts they might have.
I believe you can safely exa$$anonymous$$e a mesh straight off a prefab, and $$anonymous$$aterials are the only oddballs.
Also believe you can safely Instantiate, SetActive(false)
, and use the object as a reference. Not sure if you need to Destroy scripts, but I think I've done that. One reason people think you can't use inactives is that you can't GameObject.Find
them. But if you preserve a link, inactives work like normal.
Thanks a lot, really :) I wish i could upvote your answer but i don't have reputation enough yet :(
Your answer
Follow this Question
Related Questions
How can I set the material on an instance after creating it? 1 Answer
Change material inside instanced prefab, gameobject.renderer is NULL 1 Answer
change prefab material 2 Answers
How to change material without creating a new material instance? 1 Answer
Possible Unity Glitch. A few lines of code erasing prefab data. 1 Answer