我們都是到 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);
}