Resource prefabs lose material when script sets a property?

I have a number of similar (but distinct) prefabs in my Resources folder; I want to load them all when the game starts, which I can do fine. The problem is, they only work once.

When I hit the ‘play’ button, everything works fine; but once I’ve stopped the game, the next time I start it, I notice that all of my prefabs have lost their Material property. The ‘Materials’ property in ‘Mesh Renderer’ in the inspector says its Size is 1, but Element 0 is ‘Missing (Material)’.

I have found that I can prevent this from happening by commenting out the line of code that changes the mipMapBias on the mainTexture of the material of the renderer of the prefab after it’s loaded (but before it’s instantiated). But why would changing the mipMapBias on one of the prefab’s textures actually alter the prefab itself in my Resources folder, and why would that alteration break the reference to the material?

Is there anything I’m doing in a way that I shouldn’t? Is there something I don’t know about how Unity deals with Resource prefabs?

I’m using “Unity iPhone”, but so far I’m just testing this in Unity itself, not actually on the iPhone, so I thought I’d post it here.

edit: wow, it appears that even READING the mipMapBias property causes the material to become ‘missing’…

I’d guess you’re working directly off the assets instead of instantiating them? i.e., “var blah = Resources.Load(whatever);” instead of “var blah = Instantiate(Resources.Load(whatever));”

It’s not the mipMapBias in particular, it’s the fact that you’re changing something in the material at all. Doing so makes a runtime copy of the material behind the scenes, since you can’t make one material have different settings on different objects. If you try, then you actually end up with multiple materials, even though you may not realize this at first since Unity kind of hides it from you. (If you changed mipMapBias on sharedMaterial rather than material, then this doesn’t make a runtime copy of the material.) If you’re directly altering the resource, then the changes you make at runtime apply to the resource, so when you stop play, the runtime material naturally is deleted, which means it’s deleted from your resource as well. Generally speaking you do not want to work directly off your resources.

–Eric

That’s right, I was working off the asset itself (no Instantiate) for the sake of making a change that would be automatically applied to any Instantiated clones of that asset.

I didn’t expect that a loaded copy of a resource would actually make permanent changes to the file that it was loaded from. Is there any way to disable that from happening? (It wouldn’t be able to happen in the web player, for instance; is there any way of emulating that behavior in the editor?)

Alternately, is there any useful way of making an ‘original copy’ that it’s safe to prepare for being used to instantiate objects, without actually having that ‘original’ object instantiated and sitting in your world somewhere? (Or are we meant to have these templates sit in our world, but then recursively deactivate them, and recursively activate their clones?)

That’s the thing: without using Instantiate, it’s not a copy, it’s just a pointer to the file.

–Eric

So the only way to make runtime-only changes to a resource is to instantiate it somewhere in the game world, make your changes to that clone, and clone all the instances you actually want off of that extra one? You can’t just make a copy in memory that doesn’t get added to the display list?

Also: why does it break the reference when I read a property of a loaded resource’s material, but not when I read a property of an object in the game world? I just noticed a similar thing happens when I try to read a property from a material of a prefab associated to my script in the Inspector. Why are game world objects able to reset their references when the play stops, but prefabs lose their references when I even (successfully!) read information about them?

Not that I know of, although what you’re doing is rather different from what I’ve used Resources.Load for, so I’m pretty much at the end of my knowledge in this area.

–Eric

I’m having a similar problem. I’m trying to change the color of an instance of a prefab through scripting. It works fine – once – but when I exit and start the game, the material is missing. As a workaround, would it be efficient to assign color and material in the script each time I instantiate a prefab instead of setting the material in the inspector?

If so, how do you assign the material in Javascript?

Thanks for the help. :roll:

You can assign the material using the renderer.material property:-

var newMat: Material;
   ...

renderer.material = newMat;

But what about Editor scripts? I have an editor script that changes the sharedMaterial property of a prefab’s Renderer (I use sharedMaterial because trying to use the instanced material throws memory leak errors), but the prefab’s material still gets marked as “instance” at runtime, and upon stopping the game the materials are deleted from the prefab.

Is there some way stop the materials from being marked as “Instanced” when the game is played?

If it’s any help, I noticed that the material only gets marked as “Instanced” when the following is executed at runtime:

icon = (Texture2D)this.tileManager.renderer.material.mainTexture;

Which shouldn’t mark it as instanced, right? Unless… Yup, accessing the sharedMaterial instead doesn’t mark it as instanced.

icon = (Texture2D)this.tileManager.renderer.sharedMaterial.mainTexture;

does not mark the material as Instanced.

This is a bummer, because it means I’m going to have to make a new material for each and every prefab.