区别点
-
sharedMaterial表示共享材质,修改共享材质会改变所有使用该材质的物体,并且编辑器中的材质设置也会变
-
material表示材质实例,修改材质仅会改变物体的材质,如果该材质被其他渲染器使用,将克隆该材质用于当前的渲染器
-
每次引用Renderer.material的时候,会生成一个新的material在内存当中,需要注意内存泄漏。销毁物体的时候需要手动销毁material(Destroy(material)),或者在切换场景的时候调用Resources.UnloadUnusedAssets也可以删除该材质。
实例化出的Material无法使用Resources.UnloadAsset()销毁,因为Resource.UnloadAsset是卸载asset资源,实例化出的Material需要通过Destory来销毁。
4.调用Renderer.sharedMaterials 相同的两个材质球 ,返回的也是两个,如下图,
源码解析
源码说明
Renderer会有一个m_Materials数组,Instantiate的GameObject的Renderer组件中m_Materials引用了materials依赖资源
sharedMaterials属性的get是拿到m_Materials,sharedMaterials属性的get是传入参数0 拿到m_Materials[0] 第一个材质
materials属性的get是会Instantiate一份m_Materials出来,并且将m_Materials = (Instantiate)m_Materials,material属性的get是拿到的是重新赋值后的m_Materials[0]
伪代码
class Renderer { Material[] m_Materials; //GameObject Instantiate出来之后 他是引用了依赖的Material Material[] sharedMaterials { get{ return m_Materials; } set{ m_Materials = value; } } Material sharedMaterial { get{ return m_Materials[0]; } set{ m_Materials[0] = value; } } Materials[] materials { get{ Material[] instMats = new Material[m_Materials.length]; for(int i=0;i<m_Materials.Length;i++) { Material newMat = 实例化m_Material[i]出来 instMats[i] = newMat; m_Materials[i] = newMat; //注意后面sharedMaterials拿的是重新赋值后的 } return instMats; } set{ m_Materials[0] = value; } } Material material { get{ Material instMat = 实例化m_Material[0]出来 m_Materials[0] = instMat; //注意后面sharedMaterial拿的是重新赋值后的 return instMat; } set{ m_Materials[0] = value; } } }
实际运用注意点(优化点)
-
material字段会产生新的资源,要注意销毁
-
改变sharedMaterial不会产生内存消耗,但是会把内存中原始的Material资源更改掉
-
//我们需要改变共享材质,但是在编辑器模式下 我们不想原始资源频繁被改动 可以这样子 public static Material GetMaterial(Renderer render) { #if UNITY_EDITOR return render.material; #else return render.sharedMaterial; #endif }
-
如果是主角这一类gameobject身上需要修改材质的属性或者shader属性比较多的时候,可以第一次使用material,这样可以动态的生成一个material实例,然后再使用sharedmaterial,动态的修改这个新生成的material,而且不会创建新的material
-
一般情况下,资源属性的改变情况都是固定的,并非随机出现。比如,假设GameObject受到攻击时,其Material属性改变随攻击类型的不同而有三种不同的参数设置。那么,对于这种需求,我们建议你直接制作三种不同的Material,在Runtime情况下通过代码直接替换对应GameObject的Material,而非改变其Material的属性。
但是注意动态替换Material sharedMaterials[0] = 某个材质 是不生效的 正确使用如下
Renderer psr = gameObject.GetComponent<Renderer>(); psr.sharedMaterials[0] = newMat //错误 不会生效 //正确赋值 Material[] arrMat = psr.sharedMaterials; arrMat[0] = newMat; psr.sharedMaterials = arrMat; //单独赋值material也是生效的 psr.sharedMaterial = newMat //解析: //sharedMaterials的get方法返回的引用是const修饰的常引用,可能这个造成了无法修改 或者是sharedMaterials的get方法返回的一份拷贝的材质数组 //sharedMaterial拿到的是m_Materials数组的第一个元素 //二者set的方法都是对数组进行重新赋值
参考链接
Mesh中 material 和 sharedMaterial 的区别及内部实现的推断 (看了源码,作者推断的是正确的)