Unity 5's Asset Bundles and Materials

The Setup

We have a few projects in the shop that we've been maintaining for quite some time now. One of these projects recently had to be upgraded from Unity 4.x to Unity 5.x. And as is typical when doing a major version upgrade of a Unity project, everything went smoothly. Nary a problem was had. High fives were exchanged, backs were patted, strangers stopped by to congratulate us on another painless project upgrade.

Okay, so that's not entirely true.

 

The Hangup

In actuality, the usual problems were encountered. Plugins needed upgrading and code needed updating to meet changed APIs. That was all expected, not a big deal. There was however, one unexpected issue we faced: a multitude of magenta materials. Usually indicative of materials missing their shaders.

How it should have appeared (left) vs. how it actually appeared (right).

How it should have appeared (left) vs. how it actually appeared (right).

As it turns out, all of our pink problems shared one thing in common: Asset Bundles. The materials that were being loaded into our app from asset bundles all had this issue. We also noticed that while in Play Mode inside the Unity editor, when selecting one of the offending materials, we saw in the inspector that it appeared to have the proper value set for the shader. However, when opening the shader dropdown and re-selecting the same shader, the pink disappeared. All looked as it used to pre-upgrade.

 

The Fix(up?)

Since we found that re-applying the same shader via the Inspector panel worked while in Play Mode, we thought we'd try doing it programmatically. So in an Awake call we simply grabbed the name of the shader from the material, did the lookup for it and applied the value back to the material, like so:

Material mat = GetComponent<Renderer>().sharedMaterial;
mat.shader = Shader.Find(mat.shader.name);

So that worked. But if you're going to call something like that a lot (like we were), you're going to want to optimize. We opted to implement a lazy lookup to cut down on the amount of work the app needed to do every time we loaded up some new UI, for example.

private static Dictionary<string, Shader> sz_ShaderLookup = new Dictionary<string, Shader>();

public static Shader GetShader(string _shaderName)
{
  Shader shader;
  
  if (sz_ShaderLookup.ContainsKey(_shaderName))
  {
    shader = sz_ShaderLookup[_shaderName];
  }
  else
  {
    shader = Shader.Find(_shaderName);
    sz_ShaderLookup.Add (_shaderName, shader);
  }
  
  return shader;
}

As far as I can tell, this issue has been hanging around since Unity 5 was released. It has been reported to the good folks at Unity. If you'd like, you can mosey on over to their Issue Tracker and vote up this problem. Also worth mentioning is that the issue appears to be restricted to the Editor only. We haven't seen it occur in our Android or iOS builds. This fact may explain why the issue still exists at all, as it's not a total show-stopper.

Have you run into the same problem? Let us know if this helped or whether you took a different approach.