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: alt text

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.

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.