- Home /
The question is answered, right answer was accepted
Directly assigning Material through Renderer.materials doesn't work
Hi there,
I'm trying to assign materials to a game object through C# script. It all works fine when I assign through Renderer.material, even if the game object has multiple Renderers on it (I get all Renderers in all child objects). However, when the Renderer has multiple materials, iterating through and assigning through Renderer.materials does not change the materials!
Here's the code I'm using (renderers is an array of the Renderers on the game object):
public void SetMaterials(Material newMaterial) {
for(int i = 0; i < renderers.Length; ++i) {
// renderers[i].material = newMaterial; <-- THIS WORKS, BUT ONLY SETS THE FIRST MATERIAL!
Material[] materials = renderers[i].materials;
for (int j = 0; j < materials.Length; ++j) {
materials[j] = newMaterial; <-- THIS DOES NOT AFFECT THE MATERIALS
}
}
}
Does someone have an idea what the problem is?
Thanks a lot in advance! :)
Answer by Harinezumi · Jul 20, 2015 at 11:38 AM
OK, so I actually realised what the problem was while writing the question, but I wanted to post it anyway so that if someone else runs into it, they can find the solution :)
You cannot assign a Material to a Renderer directly through Renderer.materials[i], you have to create a separate array of Materials, assign the wanted values to that, and then set that array for Renderer.materials. Like this:
public void SetMaterials(Material newMaterial) {
for(int i = 0; i < renderers.Length; ++i) {
Material[] materials = new Material[renderers[i].materials.Length]; // <-- CREATING THE TEMPORARY ARRAY
for (int j = 0; j < materials.Length; ++j) {
materials[j] = newMaterial;
}
renderers[i].materials = materials; // <-- ASSIGNING THE WHOLE ARRAY
}
}
The reason is that Renderer.materials does not directly access the array of materials in the Renderer, it returns a temporary copy of the array, so whatever you assign to that will be in vain (it is good for reading info though). This might seem unintuitive, but from a programming point of view is the right thing to do.
I hope this helps others! :)
I encountered the exact same issue and your post gave the answer :).
Thanks to you, I won't loose any time (nor hair)!
Answer by Danor · Jul 11, 2017 at 03:08 PM
First of all thanks to Harinezum for the original! My issue was that i needed specific materials changed and some unchanged, so i made this variation and leaving it here for anyone who needs this.
(You have to add (Instance) to the name or it won't work)
public Material newMat; // is the newMaterial
private Material keepMat;
public Renderer[] renderers;
public void SetMaterials(Material newMaterial)
{
for (int i = 0; i < renderers.Length; ++i)
{
Material[] materials = new Material[renderers[i].materials.Length]; // <-- CREATING THE TEMPORARY ARRAY
for (int j = 0; j < materials.Length; ++j)
{
if (renderers[i].materials[j].name == "DontChangeYourMat (Instance)") {
oldMat = renderers[i].materials[j];
materials[j] = oldMat;
}
else if (renderers[i].materials[j].name == "YourMaterialName(Instance)") materials[j] = newMaterial;
/* add more else if's for more mats or use a switch if you need to change tons of them */
}
renderers[i].materials = materials; // <-- ASSIGNING THE WHOLE ARRAY
}
}