问题描述:
当粒子系统中只有renderer材质,没有拖尾材质时,尝试遍历并修改粒子系统下的材质时,粒子系统下的材质会出现两份。
问题结果:
在特定情况下,由于多存在一份材质,美术效果不如预期,通常表现为过爆。
问题截图:
预期效果:
由于我们项目中,需要遍历角色上的物件,统一修改材质信息。所以需要可以正确修改粒子系统中的renderer材质。
问题原因:
- 在代码中通过renderer访问材质有两种方法 .material和 .sharedMaterial。
- 使用material时,unity为了保护原有材质不被修改,会机智地复制一份原有的材质,并填充入材质数组中,并尝试替换掉原有的材质。该行为只会发生一次。
- 使用sharedMaterial则如其名,会直接修改原有的材质,该行为会影响所有引用该材质的物件。
- 遍历粒子系统下材质会发现有两份,一份是renderer的渲染材质,另一份是勾选拖尾(Trails)后出现的拖尾材质。即使在拖尾效果不勾选的情况,通过遍历的方式仍然会访问到拖尾材质。
问题结论:
Unity早期的粒子系统中(笔者使用的是5.6,确认在2018中已经不存在该问题),renderer下的拖尾材质受外部拖尾模块的勾选状态控制。在不开启拖尾效果时,我们常常不会设置该材质,但当我们遍历修改粒子系统下的材质时,却可以访问到拖尾材质,就会因为上述问题原因1而被Unity创建出一个新的拖尾材质。
所以当需要遍历修改粒子系统的材质时,导出的粒子系统需要设置拖尾特效,即使我们不需要它,也要反勾选用于占位。如下图所示,当添加该占位拖尾材质后,再尝试修改粒子系统中的render材质时,就不会发生填充拖尾特效的情况。
问题探索1:material与sharedMaterial
当两个物件使用同一材质时,使用material方式修改材质只会修改物件自身的材质,原理是Unity将原有材质复制一份并替换掉了原有材质。
实验1:修改material
尝试修改materila时,Unity可以正确复制原有材质并替换掉原有材质,在命名后面多出的(Instance)说明了这一点。
实验2:两个物件公用同一材质,通过material修改其中一个。初始2个cube皆为绿色。
private void changeMaterial()
{
MeshRenderer meshRenderer = cube_1.GetComponentInChildren<MeshRenderer>();
meshRenderer.material.color = Color.red;
}
实验3:两个物件公用同一材质,通过sharedMaterial修改其中一个。初始2个cube皆为绿色。
private void changeSharedMaterial() {
MeshRenderer meshRenderer = cube_1.GetComponentInChildren<MeshRenderer>();
meshRenderer.sharedMaterial.color = Color.red;
}
问题探索2:在Editor中修改sharedMaterial
承接上述实验3,发现在编辑器中运行时,修改sharedMaterial会影响本地的材质文件并被持久化保存,所以在编辑器中运行时,可用如下方案避免原有材质文件不被误改:
- 在版本控制中分开提交材质文件,在不该提交材质文件的提交批次中,回滚调试后的材质文件
- 不影响调试的情况下,利用UNITY_EDITOR宏进行区分,替换为通过material修改调试
#if UNITY_EDITOR
return render.material;
#else
return render.sharedMaterial;
#endif
问题总结:
- 如有遍历粒子特效的材质的需求,即使没有开启拖尾效果,也需要提供一个dummy的拖尾材质,防止unity自动创建拖尾特效。
- 修改material和sharedMaterial行为不同:
- 通过material修改会引起在内存中创建一份新的material实例,需要注意内存管理;
- 通过sharedMaterial修改会导致所有公用同一材质的物件都受影响,在编辑器模式下,会导致材质文件永久被更改。