how do i create material instances without errors

Is is possible to create an instance of a material within the editor? sometimes i get “New Material (Instance)” just by playing around with it, i wanted to know if i can actually do it on purpose, i would like to create a single material apply it to a bunch of objects and then change some properties individually without having to create a new material

i also want to know how to do the exact same thing with scripts without getting the leak error, basically so far I’ve been creating instances of a material by simply running a renderer.material to force the instance, but then i get the leak errors, what is the trick to not leak, i know that line only runs once but that doesn’t stop unity to error out

I know this thread is old, but here’s a little piece of code for those who find this thread looking for the same answers I did. Create a C# script, call it MaterialInstancer, and place it in a folder called ‘Editor’ then copy and paste this code:
using UnityEngine;
using System.Collections;
using UnityEditor;

public class MaterialInstancer : Editor {

	[MenuItem("GameObject/Instance Material")]
	public static void Instance()
	{
		if(Selection.activeGameObject == null || Selection.activeGameObject.GetComponent<MeshRenderer>() == null){
			Debug.LogError("No Valid Object Selected");
			return;
		}
		
		Material mat = Selection.activeGameObject.GetComponent<MeshRenderer>().sharedMaterial;
		Selection.activeGameObject.GetComponent<MeshRenderer>().sharedMaterial = new Material(mat);

	}

}

All you need to do is select a gameobject in the scene with a meshrenderer, and go to ‘GameObject/Instance Material’. This will create an instance of the material on the selected gameobject, and allow you to edit it without changing any of the other materials.

The trick is to set sharedMaterial, rather than Material.

Example of how to create material here: Unity - Scripting API: AssetDatabase.CreateAsset

Hey :wink:

I had the same problem as you did - Just tried out some things to use different materials / colors for objects, I created with an editor script… I constantly ran into this “Leak-Error”.

Now I just found a solution that worked for me (in Unity4) without getting this leak error, but with having the benefit of using different colors for different objects with sharedMaterial.

I have quickly written a short demo:

using UnityEngine;
using UnityEditor;
using System.Collections;

public class MyEditorScript : Editor 
{
    // If I want to have new Objects with different colors:
    [MenuItem("Test/Create Object with Material")]
    public static void CreateDemoObjects()
    {
        // Create a new GameObject with a material...
        GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
        obj.name = "TestObject";
        // Important: sharedMaterial, not material.
        obj.renderer.sharedMaterial = new Material(Shader.Find("Diffuse")); // REF1
        obj.renderer.sharedMaterial.color = Color.green;
        
        GameObject obj2 = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        obj2.name = "TestObject2";
        obj2.transform.position = new Vector3(2f, 0f, 0f);
        obj2.renderer.sharedMaterial.color = Color.red;
    }
    
// If I just want to change the material, you might use the color of an existing Object
//    public void JustASnippet()
//    {
//        // GameObject _target -> Your exiting target object...
//        Material mat;
//        // Still using sharedMaterial:
//        mat = new Material(_target.renderer.sharedMaterial);
//        mat.color = Color.blue; // Taking blue color for demo...
//        
//        // Work with "mat" here...
//        // e.g.: obj.renderer.sharedMaterial = mat;
//    }
}

Just try it out in an empty scene → Drop this Script in an “Editor” Directory. Then activate it by Menu: Test->“Create Object with Material”.

As you can see, I am creating new objects and change their color (you can play around with this to change the whole material or whatever you need) without using the material propertie.

Find the “// REF1” comment → If you comment this line out, you will get two red objects, so this line actually did the trick. (I get a green cube and a red sphere.)

I don’t know why, but this “Leak-Error” is always called when using (even reading) the “material” property. (It is not called for creating a “new Material()”)

There is only one problem with this code, I have figured out for now:
After creating the two demo objects, all the next objects, you will create (Menu: GameObject->Create Other->Cube) in this scene are red by default. (sharedMaterial was set to red at the end of the CreateDemoObjects() Function.)

Also take a look at the JustASnippet() Function, where are some lines, that worked for me too. (I created a new Material based on the sharedMaterial of an already existing object.) This Function-Code is not complete, but I think you can see what I mean.

Just to mention it: If you don’t want to create a new Material via Script, you can create it in the Editor and use this one…

I hope my code helped you to avoid the “Leak-Error”.
If I figure out more about this topic, I will update my content.

Yours
SinPin

Hi there,

I am aware that this thread is old, but I came here to look for a solution and was not completely happy with the provided answers (although I wrote one of them myself with another account 8 years ago :stuck_out_tongue: ). I liked the answer from @Kurdle_4855, but I had the impression, that the material instance was gone when restarting the editor. To avoid the “leaking material” issue and to work with instanciated prefabs, I came up with my own (second) solution, that I wanted to share with you (or myself in the future :smiley: ).

Basicly, the code does not really instanciate the material, but creates a copy from the original material, that can be shared via .sharedMaterial property without changing the appearence of other elements.

using UnityEditor;
using UnityEngine;

public class MaterialInstancer : Editor
{
	// This function creats a new material based on the one, that is referenced to the selected object(s) and saves this new material into a sub directory close to the original material.
	// Note: If you want to change a prefab, instanciate the prefab in a scene, use the InstanceMaterial for alle materials that you want to create an material-instance, and apply the prefab changes to the prefab.
	// If you do changes to a prefab directly in the prefab editor, the changes will be lost as soon as the prefab-editor is closed.
	[MenuItem("GameObject/Create Material Instance", false, 1111)]
	public static void InstanceMaterial()
	{
		foreach(Transform selectedTrans in Selection.transforms)
		{
			if (selectedTrans != null)
			{
				MeshRenderer selectedMeshRenderer = selectedTrans.GetComponent<MeshRenderer>();
				if(selectedMeshRenderer != null)
				{
					Material selectedMat = selectedMeshRenderer.sharedMaterial;
					if(selectedMat != null)
					{
						// Create Material:
						Material materialInstance = new Material(selectedMat);

						// Save material to assets:
						string parentDir = AssetDatabase.GetAssetPath(selectedMat); // returns something like Assets/Materials/matName.mat
						parentDir = parentDir.Substring(0, parentDir.Length - (selectedMat.name.Length + ".mat".Length + 1)); // Results in Assets/Materials
						string instanceFolder = selectedMat.name + "Instances";
						string saveMatInstancePath = parentDir + "/" + instanceFolder;
						if(AssetDatabase.IsValidFolder(saveMatInstancePath) == false)
						{
							string guid = AssetDatabase.CreateFolder(parentDir, instanceFolder);
							string newFolderPath = AssetDatabase.GUIDToAssetPath(guid);
							Debug.Log("[Create Material Instance] Created new directory for instanced material: " + newFolderPath);
						}

						// In case, material already exists, add number to its name:
						string assetName = selectedMat.name + "Instance";
						string[] searchPaths = new string[] { saveMatInstancePath };
						int count = 0;
						while(AssetDatabase.FindAssets(assetName, searchPaths).Length > 0)
						{
							assetName = selectedMat.name + "Instance" + (++count).ToString();
						}

						// Save asset:
						AssetDatabase.CreateAsset(materialInstance, saveMatInstancePath + "/" + assetName + ".mat");

						// Link material to game object:
						selectedMeshRenderer.sharedMaterial = materialInstance;

						Debug.Log("[Create Material Instance] " + selectedTrans.name + "s material got instantiated successfully.", selectedTrans.gameObject);
					}
					else
					{
						Debug.LogWarning("[Create Material Instance] Selected element does not have a material that can be instanciated.", selectedTrans.gameObject);
					}
				}
				else
				{
					Debug.LogWarning("[Create Material Instance] Selected Element does not have a MeshRenderer.", selectedTrans.gameObject);
				}
			}
			else
			{
				Debug.LogWarning("[Create Material Instance] Something went wrong, the selected gameObject does not exist.");
			}
		}
	}

	[MenuItem("GameObject/Create Material Instance", true)]
	public static bool CheckInstanceMaterial()
	{
		return (Selection.transforms != null && Selection.transforms.Length > 0);
	}
}