- Home /
Changing one material instance affects all instances?
I am trying to instantiate a prefab gameobject using 'Instantiate(myObject);', of which i need a lot more than one. For some reason though i can't seem to create new instances of the gameobject. Each 'new' instance is a clone of an existing one. I need the instances to be fresh instances instead of clones, because i want to be able to change a texture by code and having the code apply to one instance instead of all instances. Whenever I change the texture on the object the texture changes for all objects.
The code I use looks somewhat like this:
ObjectController object = gameObject.AddComponent<ObjectController>();
Instantiate(object.Object);
The ObjectController contains a gameobject called Object, which is loaded like this:
this._object = (GameObject)Resources.Load ("Models/object");
For some reason this code looks fine and it spawns no errors / doesn't cause any problems. How do I go about trying to accomplish this?
Do I really need to create separate prefabs with preselected textures for each instance i want to create?
EDIT:
I found out it's not necessarily a problem with clones of the object, as much as a problem with shared materials. For some reason every instance of the object uses the same material, or links to the same material. I've been searching google on how to create a new instance of a material and assigning it to an object, but somehow none of the results seemed to work for me.
So I guess my question remains sort of the same, except it doesn't involve gameobjects but materials.
EDIT 2:
I know this question is related to other questions that have frequently been answered on this board, however as you say, by accessing a renderer's material it creates a new instance of the material does not apply to me.
I've tried changing the material's textures by using the following code:
foreach(Material material in this.object.renderer.materials) {
if(material.name == "face") {
material.mainTexture = this.replacementTexture;
}
}
I obviously access the renderers material here, but still all the object instances change textures instead of just the one i perform the texture change on. I know I am probably overlooking something but I wasn't able after a couple of hours of searching for a solution (both on this board as well as in other locations) to resolve this problem.
Instantiate can clone objects in the scene, or it can clone prefabs. If you want a fresh GameObject, you can always clone a prefab that's not even in the scene.
As far as creating material instances, Unity usually handles that for you. The first time you access a renderer's material, an instance will automatically be created and assigned to the renderer.
I'm hesitant to let this through the moderation queue because it's a frequently asked question. It's not a bad question, we're just trying to cut down on the number of duplicate threads in this area of the site. If you have anything else to add, feel free to comment here; if not, I'll drop the question from the queue in a little while.
If you can't find help, here, you can always try the forums or look up some tutorials online.
Your problem is somewhere else, then. Perhaps your code is accidentally editing more materials than you think, or you're editing the material of an object which you later clone. If you edit the "base" object, any clones made from it will also be affected.
Answer by Nemarques · Jun 18, 2017 at 03:26 PM
Hey @Tietsap i suggest you to take a look at this. http://thomasmountainborn.com/2016/05/25/materialpropertyblocks/ You just have to mark on your shader the properties that will differ. And then in the script you can manipulate the values of each object despite of being the same material.
You also have here some documentation by unity https://docs.unity3d.com/ScriptReference/MaterialPropertyBlock.html
Answer by bloodthirst69 · Jul 24, 2018 at 07:15 PM
So i was fiddling a bit on my own since i needed this , and it was actually much simpler than i thought , here's a code example :
public class ButtonColorSelector : MonoBehaviour {
public Color MainColor;
public Color SecondaryColor;
// Use this for initialization
void Start () {
GetComponent<Image>().material = Instantiate<Material>(GetComponent<Image>().material);
}
private void Update()
{
GetComponent<Image>().material.SetColor("_ButtonColor", MainColor);
GetComponent<Image>().material.SetColor("_Color1", SecondaryColor);
}
}
Just set the original Material from the inspector and each instance will have its properties set by the script , in my example the material was attached to 4 diffent Images with the same material but different properties BAM done !
Cool example but I need to fix your code just in case someone would copy-paste it in a real project :
// Within an Example.cs file
// We requirement this component to have an Image nearby, otherwise Unity will create it
[RequireComponent(typeof(Image))]
public class Example : $$anonymous$$onoBehaviour
{
// Better encapsulation
[SerializeField] private Color _mainColor;
[SerializeField] private Color _secondaryColor;
private Image _image;
private void Start()
{
// We do this GetComponent() only once
_image = GetComponent<Image>();
if (_image.material != null) // We dodge the null ref exception
_image.material = Instantiate(_image.material);
}
private void Update()
{
// GetComponent() is a costly operation, doing it twice in a frame was rough for your game ;)
_image.material.SetColor("_ButtonColor", _mainColor);
_image.material.SetColor("_Color1", _secondaryColor);
}
}
Answer by toddisarockstar · Jun 18, 2017 at 03:45 PM
you need to create a new material instance instead of just changing the texture of the current one. i would recommend droping an alternative material into the inspector for use. but if you wanted to create it at runtime you can make it like this. this will work and only apply to one object
someobject.renderer.material = new Material(Shader.Find("Diffuse"));
someobject.renderer.material.color = Color.blue;
someobject.renderer.material.mainTexture = sometexture2D
Answer by naklow12 · Oct 10, 2019 at 11:55 AM
Material material = new Material(Shader.Find("Shader Graphs/SHADER_NAME_HERE"));
material.SetColor("Color_C6F9B478", Color.blue); // CHANGE Color_C6F9B478 with your Color prop ID
gameObject.GetComponent<Renderer>().material = material;
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
[C#] Checking InstanceOf? 2 Answers
How can I set the material on an instance after creating it? 1 Answer
Is it possible to calculate batch count of each material ? 0 Answers