(八)RectMask2D详解

1.前言

RectMaskD的基本原理就是CanvasRenderer的EnableRectClipping方法,上一节已经做了详细说明。而它的工作流程在(六)(五)中也做了详细分析。此篇重新梳理一下流程,做更细致的分析。

2.详解

RectMask2D的基本原理比较建议,复杂点在于其上层逻辑比较复杂,今天就按逻辑顺序进行分析。
1)启动时通过ClipperRegistry.Register(this);将自己注册到RectMask2D的管理类ClipperRegistry中,便于后续统一调用(CLipperRegistry参与整个Canvas的运作流程,所以可以参考此文的流程图以及2.3节的分析)。
2)启动的同时通过 MaskUtilities.Notify2DMaskStateChanged(this)通知所有子游戏物体(继承IClippable,后续简称子Clippable)重新更新Clipp状态(通过UpdateClipParent重新确定影响自身Clip的RectMask2D);由于考虑到会存在多个Canvas以及RectMask2D的情况,所以子Clippable在得到重新更新状态通知时,会调用MaskUtilities.GetRectMaskForClippable方法重新确认RectMask2D。确认后每个子Clippable将自己添加到相应的RectMask2D维护的列表中。

        public static void Notify2DMaskStateChanged(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 IClippable; if (toNotify != null) toNotify.RecalculateClipping(); } ListPool<Component>.Release(components); } 

以上两步为逻辑层控制实现子游戏物体mask的基础。后续是实现mask的方法。


3)当Canvas更新时会调用ClipperRegistry的cull方法进行剔除(即实现遮罩),如下所示。cull方法会通知所有的RectMask2D进行PerformClipping。

       public void Cull() { for (var i = 0; i < m_Clippers.Count; ++i) { m_Clippers[i].PerformClipping(); } } 

4)当RectMask2D收到PerformClipping命令时,先获取所有父类有效的RectMask2D。这是为了后续计算遮罩的范围Rect。因为当有两个RectMask2D时,裁切范围是两个共同作用的区域。然后采用Clipping.FindCullAndClipWorldRect方法计算裁切区域。通过名字也可以知道,计算出来的rect为world级别的(其实就是对应的Canvas下的坐标值)。

       public static Rect FindCullAndClipWorldRect(List<RectMask2D> rectMaskParents, out bool validRect) { if (rectMaskParents.Count == 0) { validRect = false; return new Rect(); } var compoundRect = rectMaskParents[0].canvasRect; for (var i = 0; i < rectMaskParents.Count; ++i) compoundRect = RectIntersect(compoundRect, rectMaskParents[i].canvasRect); var cull = compoundRect.width <= 0 || compoundRect.height <= 0; if (cull) { validRect = false; return new Rect(); } Vector3 point1 = new Vector3(compoundRect.x, compoundRect.y, 0.0f); Vector3 point2 = new Vector3(compoundRect.x + compoundRect.width, compoundRect.y + compoundRect.height, 0.0f); validRect = true; return new Rect(point1.x, point1.y, point2.x - point1.x, point2.y - point1.y); } 

其中比较有用的一个方法是计算两个rect的相交范围:

        private static Rect RectIntersect(Rect a, Rect b) { float xMin = Mathf.Max(a.x, b.x); float xMax = Mathf.Min(a.x + a.width, b.x + b.width); float yMin = Mathf.Max(a.y, b.y); float yMax = Mathf.Min(a.y + a.height, b.y + b.height); if (xMax >= xMin && yMax >= yMin) return new Rect(xMin, yMin, xMax - xMin, yMax - yMin); return new Rect(0f, 0f, 0f, 0f); } 

5)当确定了裁切范围后,RectMask2D通知自己维护的IClippable列表成员进行裁切,然后每个IClippable列表成员调用 canvasRenderer.EnableRectClipping(clipRect);进行裁切。

以上为基本流程,真是代码中会考虑其他一些状况。比如第五步并非一定会进行裁切,而是会根据条件选择裁切或者不进行裁切。

3.结语

以上为RectMask2D裁切的详细流程分析。

猜你喜欢

转载自www.cnblogs.com/llstart-new0201/p/12678699.html