- Home /
Modifying material modifies the actual .mat file!
Ok, I've encountered some very weird behavior, and wondering if it's designed like this or a bug, and if anyone has ideas of workarounds.
I'm trying to change the texture of a material, that will affect all the renderers that use this material. What I did, was assign the Material
property of my script through the editor
public Material someMat;
And then within the game I use:
someMat.mainTexture = otherTexture;
Now, everything is fine, the new material is used, and then I stop the game. Lo and behold, in the editor, after stopping the game, I still see all the renderers using the updated material! When I check in my file system, I see that the script actually changed the .mat file, not just for runtime, but permanently.
Is this the planned behavior or a bug? I thought only editor scripts modify actual resource files. Can anyone suggest a way to do this other than going through all the renderers and modifying them one by one?
Answer by Bunny83 · Jul 06, 2013 at 09:50 AM
When you create a Material in your project it will be an asset saved to the .mat file. This asset can be used on as much renderers as you like. All those renderers will use the material as sharedMaterial. That means all renderers just reference the material you assigned to them which is in your case the material asset inside your project folder.
Accessing the .material property in game will create a duplicate of the used material only for this renderer. This actually disconnects the renderer from the shared material.
What might confuses you is the fact that changes to the material are preserved when you leave playmode. That's because only things that are saved to the scene is reverted when leaving playmode. Unity saves the scene to disk when you enter playmode and restore it afterwards. All assets you reference directly are changed directly. This is also true for prefab-assets. Just try it. Create a prefab of a cube, reference the prefab in a script of yours and change the position of the prefab. When you leave playmode you will notice that the prefab will have the new position you've just set in-game.
That's why you should avoid changing assets directly. In a built game that's not a real problem since all assets are stored in your build and loaded into memory when the game starts. Changes to assets will be lost once you restart your game.
However in the editor you can change any asset at any time. The only difference between editor scripts and ingame script is that editor scripts have an additional API set (the UnityEditor namespace) which is only available inside the editor. In general all script can do the same thing since they all run in the same mono environment. In-game script could also use editor stuff, but they are compiled without the UnityEditor reference so all the stuff in there is not known at compile time. Also it wouldn't make sense to do something like that since you can't build your game when you use UnityEditor stuff.
Ok back to the topic: What you can do is find all renderers which are using the material in question, create a duplicate in memory and assign this duplicated Material to all renderers sharedMaterial property.
Something like that:
// C#
public Material matAsset;
void Start()
{
var allRenderers = (Renderer[])FindObjectsOfType(typeof(Renderer));
var matInstance = new Material(matAsset);
foreach (Renderer R in allRenderers)
if (R.sharedMaterial == matAsset)
R.sharedMaterial = matInstance;
}
Thank you, very informative and helpful. In my solution, I'm setting the default texture at the beginning of the game. Do you expect any performance hits?
I still think it's weird, even more now that you tell me that you'll get different behavior in editor than actual player...
How can I edit mat file directly ? I google many tool but I don't find any tool which can edit mat file
Is this a wise thing to do in builds, or only in the Editor? If it doesn't help in builds, then would it be better not to do this, as it just uses more resources? Would it be better to do this:
#if UNITY_EDITOR
$$anonymous$$aterial mat = new $$anonymous$$aterial(matAsset);
#else
$$anonymous$$aterial mat = matAsset;
#endif
Your answer
Follow this Question
Related Questions
Changing two different objects renderer colour 1 Answer
How to switch the texture being used by the material of the mesh renderer 1 Answer
How to change a value in custom shader through script, C# 1 Answer
How could I make the texture on one object follow the rotation of another object? 0 Answers
Standard material shader ignoring SetFloat property "_Mode"? 1 Answer