关于 Unity UGUI 中修改 Mask 组件下 Image 等子节点组件的材质无效的问题

  前几天同事做了一个效果,希望在原本使用了遮罩组件 Mask 的技能图标(让技能图标变成圆形)上在添加一个置灰的功能,但问题来了:因为是动态根据游戏中玩家的条件才动态置灰,以修改 Mask 下子节点 Image 组件的材质来实现的,但是实际上怎么修改也不起作用,呈现出的效果都只停留在第一次运行时的样子。

  一开始我也以为是 shader 的问题,修改的 property 没有生效,但是通过各种测试发现 shader 是已经修改成功的了。就是没有应用上,在查阅了各方资料无效果的情况下只能翻阅 unity 托管在 BitBucket 上的 UI 源码了(赞一个)。

  先看看 Mask.cs,通过 StencilBuffer 实现遮罩,继承自 IMaterialModifier,需要实现接口:Material GetModifiedMaterial(Material baseMaterial); 这个接口是用来修改获取的材质来实现遮罩效果。在各种 MonoBehaviour 改变时都会通过 MaskUtilities 这个静态类来向所有的子节点发送 Stencil 状态改变的消息。

  所以想知道为什么材质效果总是维持在第一次启动执行时,就看看 Mask.OnEnable 里调用的 MaskUtilities.NotifyStencilStateChanged(this); 做了什么。

 public static void NotifyStencilStateChanged(Component mask)
{
var components = ListPool<Component>.Get();
mask.GetComponentsInChildren(components);
for (var i = ; i < components.Count; i++)
{
if (components[i] == null || components[i].gameObject == mask.gameObject)
continue; var toNotify = components[i] as IMaskable;
if (toNotify != null)
toNotify.RecalculateMasking();
}
ListPool<Component>.Release(components);
}

  看以上代码可以得知,Mask 会调用所有子节点中继承自 IMaskable 组件(Image 继承 MaskableGraphic, MaskableGraphic 继承自此)的 RecalculateMasking() 函数。该函数将

MaskableGraphic 中的  m_ShouldRecalculateStencil 修改为 true,这样当开始渲染时,每个组件都会被调用 GetModifiedMaterial 以返回一个适用于当前渲染的 材质(有可能会返回一个修改过的拷贝),当 Imange.m_ShouldRecalculateStencil = true 时,会在 GetModifiedMaterial 中返回一个支持 Stencil 的修改过的材质,用于实现 Mask 遮罩效果,所以问题也很明了了,修改 Mask 下的 Image 组件原始的材质是不起作用的,因为实际渲染使用的不是它。

  那么如何修改?只需要自己继承一个比如 Image 组件,并重载 GetModifiedMaterial 方法,将基类返回材质保存即可,这就是实际渲染使用的材质,当你想修改置灰时,使用这个材质即可。代码如下:

 public class CustomImage : Image
{
public override Material GetModifiedMaterial(Material baseMaterial)
{
Material cModifiedMat = base.GetModifiedMaterial(baseMaterial);
// Do whatever you want with this "cModifiedMat"...
// You can also hold this and process it in your grayscale code.
// ...
return cModifiedMat;
}
}

  也可以去看看我在 Unity Answer 上对于该问题的回答:http://answers.unity3d.com/questions/1130203/ui-mask-override-my-shaders-custom-property.html

上一篇:execjs


下一篇:shell中的eval命令