源码15:Mask
public class Mask : UIBehaviour, ICanvasRaycastFilter, IMaterialModifier
{
[NonSerialized]
private RectTransform m_RectTransform;
[SerializeField]
private bool m_ShowMaskGraphic = true;
...
}
Mask 组件继承UIBehaviour 同时要实现两个接口ICanvasRaycastFilter, IMaterialModifier
ICanvasRaycastFilter:RectTransform是否包含从相机看到的点
public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (!isActiveAndEnabled)
return true;
return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera);
}
IMaterialModifier :继承自IMaterialModifier接口,MaskableGraphic也继承了这个接口,这个方法是用来修改获取的材质来实现遮罩效果。
与RectMask2D类似 ,Mask的使用要配合被遮罩者IMaskable,也就是MaskableGraphic组件。但是和RectMask2D组件不同,Mask必须绑定一个Graphic用来做遮罩。UGUI里显示的组件基本都是继承MaskableGraphic组件的,也就是说MaskableGraphic作为遮罩的时候就需要绑定一个Mask组件。同时Text、Image负责显示的时候又是被遮罩者。MaskableGraphic有属性做了区分。
/// <summary>
/// Is this graphic the graphic on the same object as a Mask that is enabled.
/// </summary>
/// <remarks>
/// If toggled ensure to call MaskUtilities.NotifyStencilStateChanged(this); manually as it changes how stenciles are calculated for this image.
/// </remarks>
public bool isMaskingGraphic
{
get { return m_IsMaskingGraphic; }
set
{
if (value == m_IsMaskingGraphic)
return;
m_IsMaskingGraphic = value;
}
}
isMaskingGraphic的赋值就是在绑定Mask组件的时候 ,当Mask组件激活时设置为True,关闭的时候设置为False。
Mask代码:
protected override void OnEnable()
{
base.OnEnable();
if (graphic != null)
{
graphic.canvasRenderer.hasPopInstruction = true;
graphic.SetMaterialDirty();
// Default the graphic to being the maskable graphic if its found.
if (graphic is MaskableGraphic)
(graphic as MaskableGraphic).isMaskingGraphic = true;
}
MaskUtilities.NotifyStencilStateChanged(this);
}
protected override void OnDisable()
{
// we call base OnDisable first here
// as we need to have the IsActive return the
// correct value when we notify the children
// that the mask state has changed.
base.OnDisable();
if (graphic != null)
{
graphic.SetMaterialDirty();
graphic.canvasRenderer.hasPopInstruction = false;
graphic.canvasRenderer.popMaterialCount = 0;
if (graphic is MaskableGraphic)
(graphic as MaskableGraphic).isMaskingGraphic = false;
}
StencilMaterial.Remove(m_MaskMaterial);
m_MaskMaterial = null;
StencilMaterial.Remove(m_UnmaskMaterial);
m_UnmaskMaterial = null;
MaskUtilities.NotifyStencilStateChanged(this);
}
graphic.SetMaterialDirty(); 是设置进行rebuild的标签为True 。分析Graphic是的时候讲过。
public static void NotifyStencilStateChanged(Component mask)
{
var components = ListPool<Component>.Get();
mask.GetComponentsInChildren(components);
for (var i = 0; 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);
}
MaskUtilities.NotifyStencilStateChanged(this) 通知所有实现了IMaskable的子物体重新计算遮罩。其实最终还是回到了Graphic中 调用SetMaterialDirty,更新材质UpdateMaterial,修改Graphic的顶点数据以及材质数据。这也就是为什么MaskableGraphic 需要实现IMaterialModifier的原因。