Quite often I'll use a ScriptableObject to hold this kind of shared data.
It's similar to OC_RaizW's suggestion of a MonoBehaviour, but doesn't require a GameObject to host this content - a ScriptableObject can exist as its own asset:
// This attribute makes the asset show up in the editor's Assets -> Create menu
// and when right-clicking in your Project window and choosing Create
[CreateAssetMenu(filename = "New Material Library", menuName = "Custom/Material Library")]
public class MaterialLibrary:ScriptableObject {
// You could also expose individual named fields, or provide a public method
// to retrieve a material based on an enum key or other input...
public Material[] materials;
}
Now all your edges can share a reference to this MaterialLibrary.
I often use this for collecting up pools of foley sounds that many characters use, or for sharing configuration parameters used by multiple player/enemy instances. This gives me a single place to make changes to tune all instances currently running or that have yet to be spawned. This is a form of the flyweight pattern.
Another trick I like to use for materials in particular is to prepare a general-purpose material variant cache:
public static class MaterialCache {
struct Variant {
public Material originalMaterial;
public Color32 color;
}
static Dictionary<Variant, Material> _cache;
public static Material GetMaterialVariant(Material originalMaterial, Color32 color) {
var key = new Variant{originalMaterial = originalMaterial, color = color};
Material output;
if(_cache.TryGetValue(key, out output) == false)
{
output = Instantiate<Material>(originalMaterial);
output.color = color;
_cache.Add(key, output);
}
return output;
}
// You may also want to provide a Flush method to destroy cached materials,
// in case your game ever enters a state where you don't need them anymore.
}
Now anything that wants to modify its display colour can do so like so...
public class ColorChanger : MonoBehaviour {
Material _originalMaterial;
Renderer _renderer;
void Start() {
_renderer = GetComponent<Renderer>();
_originalMaterial = _renderer.sharedMaterial;
}
public void ChangeColor(Color32 color) {
var mat = MaterialCache.GetMaterialVariant(_originalMaterial, color);
_renderer.sharedMaterial = mat;
}
}
If I'm using only a handful of easily-standardized colours like Color.Red, then every instance with the same base material asking for a red variant will get a reference to the same red material instance, rather than creating copies all over the place. And best of all, I don't have to manually create and assign all those near-duplicate materials - they're just created on demand. :)