Material leak workaround?

In my current project there seem to be some material leaks which I can’t get rid of in the first place since I don’t want the user to have to create a Material asset every time he wants to assign a new one.

My workaround so far has been that I call a function to destroy all materials I used in the end:

      ...
        
foreach(Material material in matList){      
        
    
    if(Application.isEditor){
       GameObject.DestroyImmediate(material);
    }else{
       GameObject.Destroy(material);
    }
        
}
        ...

This works fine and stops the leaks but if the user used a Material asset instead of creating a new one per code I get the following error on destruction:

“Destroying assets is not permitted to avoid data loss.”

Since I don’t want to destroy the Material asset but only those Materials that got created by code I can’t just call

DestroyImmediate(material, true);

I tried checking if the material is an asset before destruction by using

if(!AssetDatabase.Contains(material)){
     DestroyImmediate(material);
}

which worked as long as I was testing in my custom editor window or using “play” in Unity. But since it’s an editor function I can’t build it this way.

Is there any other way to get rid of the material leaks? As I said before, I can’t just stop using Materials created by code. Or is it possible to suppress the “Destroying assets is not permitted…” error? Since aside from the annoying message that works really well.

Thanks in advance

delstrega

After thinking about this stuff for hours now I discovered something interesting:
At least on my machine, materials that are created by code with

Material m = new Material(…);

had an InstanceId (returned by .GetInstanceId() ) that was negative, all material assets had a positive one.

I’m quite sure that this is no coincidence (correct me if I’m wrong here). This seems to be an easy solution to check whether a material is in fact an asset or created by code only. The destruction code now is:

  ...

foreach(Material material in matList){

if(Application.isEditor){
   if(material.GetInstanceId() < 0){
        GameObject.DestroyImmediate(material);
   }
}else{
   if(material.GetInstanceId() < 0){
        GameObject.Destroy(material);
   }
}

}

It works perfect on my machine but I don’t know if it works everywhere. It would be great if anyone could check if .GetInstanceId() returns a negative value for “= new Material” and a positive one if an asset is used.

So what you need to do is write a script that does this:

#MaterialManager.js#

 import System.Collections.Generic;
 import System.Linq;
 

  private static var existingMaterials : Dictionary.<Material, Material>;

  function Awake() {
         if(existingMaterials.Count == 0)
         {
                existingMaterials = Resources.FindObjectsOfTypeAll(typeof(Material)).Cast.<Material>().ToDictionary(function(m){return m;});
         }
         Destroy(GameObject);
  }
  
  static function DestroyMaterial(material : Material) {
       if(!existingMaterials.ContainsKey(material)) {
             DestroyImmediate(material);
       }
  }

When you want to destroy a material do MaterialManager.DestroyMaterial(myMaterial); this will only destroy materials that were created by the user.

It sounds like it might be a good idea for you to create code versions of all the asset materials during your initialization - that way you don’t have to worry about whether a material supports some action or not, and your user is “more guaranteed” to have full control over the materials.

so (i hope this isn’t a dumb answer) if you code your materials that when they are created they get a certain tag “CreatedByCode” for exemple and when you end your script do it like this
DestroyImmediate(Tag “CreatedByCode”)

You can use AssetDatabase.Contains in your script. You just need to wrap UnityEditor stuff with

#if UNITY_EDITOR
    //editor stuff
#endif

whereever you use something from the UnityEditor namespace.

So, If you have a “using UnityEditor” you have to wrap that as well or use the full class name “UnityEditor.AssetDatabase.Contains

This way you can put editor functionality into your runtime scripts and you’re still able to build. Keep in mind that your script has to work with the #if sections removed. Also keep in mind there’s also an #else