(八)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裁切的详细流程分析。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值