我们都是到 ugui 里,官方给我们提供了一个 Mask 组件,用来实现显示一些特殊形状的ui,但是有的时候,我们刚好需要和它作用完全相反的功能实现一些效果。
这里说一个即不需要改shader,还很简单的方法,聪明的你肯定也已经想到了。
对,既然Mask除了和我们要的扣图方式是反的外,好像基本实现了我们所有的需求,由此,经过观察Mask组件和与其配合的Image组件后发现。
Image 的父类里面有一个方法,有获取过Mask组件,并进行了判断,这个方法名字叫
GetModifiedMaterial
/// <summary>
/// <para>See IMaterialModifier.GetModifiedMaterial.</para>
/// </summary>
/// <param name="baseMaterial"></param>
public virtual Material GetModifiedMaterial(Material baseMaterial)
{
Material baseMat = baseMaterial;
if (this.m_ShouldRecalculateStencil)
{
Transform sortOverrideCanvas = MaskUtilities.FindRootSortOverrideCanvas(this.transform);
this.m_StencilValue = !this.maskable ? 0 : MaskUtilities.GetStencilDepth(this.transform, sortOverrideCanvas);
this.m_ShouldRecalculateStencil = false;
}
Mask component = this.GetComponent<Mask>();
if (this.m_StencilValue > 0 && ((UnityEngine.Object) component == (UnityEngine.Object) null || !component.IsActive()))
{
Material material = StencilMaterial.Add(baseMat, (1 << this.m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << this.m_StencilValue) - 1, 0);
StencilMaterial.Remove(this.m_MaskMaterial);
this.m_MaskMaterial = material;
baseMat = this.m_MaskMaterial;
}
return baseMat;
}
然后我们发现其中有一处是这么写的 CompareFunction.Equal ,就是这里决定了Mask的工作方式。
所以现在我们只需要新建一个文件 ReverseMaskedImage.cs 继承自 Image , 然后重写
GetModifiedMateria 方法,内容直接照抄上面的,把 CompareFunction.Equal 改成 CompareFunction.NotEqual
当我们拿 ReverseMaskedImage 去代替 Image 挂好图片放到 Mask 的子节点下后,惊奇的发现,Mask 组件再影响我们时,实现的正好是相反的效果,变成反向掏洞了。
但是这样不算完,虽然视觉效果对了,但是我们发现点击效果不对,继续看 Mask 代码,发现一个能重写的方法 IsRaycastLocationValid
/// <summary>
/// <para>See:ICanvasRaycastFilter.</para>
/// </summary>
/// <param name="sp"></param>
/// <param name="eventCamera"></param>
public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
return !this.isActiveAndEnabled || RectTransformUtility.RectangleContainsScreenPoint(this.rectTransform, sp, eventCamera);
}
这下就好办了,既然我们靠重写的方式,实现了视觉效果的翻转,这里我们再新建一个文件
ReverseMask.cs 并集成自 Mask ,重写这个方法,直接将条件反过来就行了
public override bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (!isActiveAndEnabled)
{
return true;
}
return !RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera);
}