- Home /
Why Instantianting material does not copy all properties
Could someone explain why calling Instantiate on material doesn't copy all properties?
I have this code:
Material oldMaterial = mat;
Debug.Log ("Old: "+oldMaterial.GetFloat("TestFloat"));
mat = Material.Instantiate(mat) as Material;
Debug.Log ("New1: "+mat.GetFloat("TestFloat"));
mat.CopyPropertiesFromMaterial(oldMaterial);
Debug.Log ("New2: "+mat.GetFloat("TestFloat"));
This would print:
Old: 0.3
New1: 0
New1: 0.3
Any ideas why?
Copying of Colors and Textures seems to work fine this way. Copying material by using render.material seems to work fine too.
I guessing that it's just because quite often, when copying a material, one doesn't want to copy all the shader properties - one just wants a new material which has the same shader.
But that makes little sense. Instantiating of anything else makes a proper copy without any exclusions, why would material copying work in other way?
I know one often wants to set a $$anonymous$$aterial up as a copy of another and then make changes to the new one, but you shouldn't ever aim to end up with or use a $$anonymous$$aterial that's an exact copy of another $$anonymous$$aterial. That makes them different.
Yes, of course I'll make some modifications after making a copy.
What you're saying makes no sense. If you use such logic you could say that instantiating a material could modify a shader on the copy or any other property...because you will change some other properties anyway...because you should never use exact copy. That's what you're saying.
How good is a copy if it's not a copy? if you don't know which properties are omitted when copying?
Answer by maccabbe · Nov 19, 2015 at 02:29 AM
Instantiating a material doesn't work as expected because instantiating a material is unexpected. Instantiating is meant to be used with GameObjects (you are also "allowed" to instantiate Components but I personally avoid doing this as you're not cloning the component but rather the gameobject the component is attached to).
"When you clone a GameObject or Component, all child objects and components will also be cloned with their properties set like those of the original object. "
http://docs.unity3d.com/ScriptReference/Object.Instantiate.html
It isn't really meant to be used by anything else that inherits from UnityEngine.Object. Instead it is expected to make new UnityEngine.Objects (that aren't gameobjects or components) using new, i.e.
Material material = new Material(Shader.Find("Transparent/Diffuse"));
http://docs.unity3d.com/ScriptReference/Shader.Find.html
Unity lets multiple renderers share one material so that programs can reduce memory usage. This is why when you change the color of your material in the inspector all objects using that material in the scene are affected. The material in your files and on your object is the same object. This is different from prefabs/gameobjects where they are all different objects and changes to one instance of a gameobject shouldn't affect another.
As you noted, if you want to make a new material that then copies all the properties from another that is possible. However, as this is unusual, it makes sense to require an explicit operation (CopyPropertiesFromMaterial).
That's an interesting take. From that perspective the problem is in API. It should have two methods ins$$anonymous$$d:
GameObject Instantiate(GameObject original);
Component Instantiate(Component original);
...but Unity API are not always rational :DD
This also make me realise that I haven't tried this:
$$anonymous$$aterial mat = new $$anonymous$$aterial(old$$anonymous$$aterial);
I wonder how well that works...
$$anonymous$$aterial mat = new $$anonymous$$aterial(old$$anonymous$$aterial);
doesn't work either. And this one must make a proper copy. So it must be a bug...
What do you mean by "it doesn't work?" I make $$anonymous$$aterials that way all the time.
I mean it doesn't copy all properties. It will copy my Color, Vector and Texture properties, but not float property.
Perhaps it would help if you explained your use case?
Usually when I'm copying a $$anonymous$$aterial, it's when I've copied an object but want to be able to make the new one look different. So I'm copying the $$anonymous$$aterial and then immediately setting its properties. If I want most of those properties to be the same as those for the object I'm copying , then I might start with CopyPropertiesFrom$$anonymous$$aterial and then just tweak the values I want changed. But often I don't do that, because I've already written a function that sets the $$anonymous$$aterial's properties up as required for that particular object.
So if having to add that one line is really a big inconvenience to you, then I'm wondering if perhaps you're using things in a way that hasn't occurred to me.
$$anonymous$$y case: I copy a material and I want to change one property. All other properties I want to stay the same.
I have no issue for calling CopyPropertiesFrom$$anonymous$$aterial, but I'm just saying that it sounds to me like a bug that calling new $$anonymous$$aterial(old$$anonymous$$aterial) doesn't perform a copy of all properties.
In your case: do you reassign a shader from old material to new material as well. If not, why do you expect that part to be copied properly?