几何向量:Ugui多边形检测

刚好有时间,就把前些天完成的一个ugui工具项目碰到的一个问题说一下。
就是ugui多边形检测的问题,估计是我很长时间没怎么做ui了,我都不知道Unity多边形检测PolygonCollider2D。哈哈哈哈,自己把这个功能写了一遍。
先上官方,如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class TestPolygon2dCheck : MonoBehaviour
{
    
    
    private PolygonCollider2D polyColl2D;

    void Start()
    {
    
    
        polyColl2D = GetComponent<PolygonCollider2D>();
    }

    private void Update()
    {
    
    
        if (Input.GetMouseButtonDown(0))
        {
    
    
            Vector2 spos = Input.mousePosition;
            if (polyColl2D.OverlapPoint(spos))
            {
    
    
#if UNITY_EDITOR
                Debug.LogFormat("TestPolygon2dCheck GetMouseButtonDown time = {0}", Time.realtimeSinceStartup);
#endif
            }
        }
    }
}

效果如下:
在这里插入图片描述
可以看得出来点击PolygonCollider2D区域就能检测出来。
当然我是自己做了一份Ugui的快速多叉树多边形检测功能,做完才看到这个PolygonCollider2D。
所以还是废物利用,来一篇博客。
首先我们来看一下平面多边形(凸)的检测(btw可能有同学要问了,那凹多边形怎么检测呢?其实凹多边形可以看做2个或以上凸多边形(或三角形)的组合,就相当于一个凹多边形要做2次或以上凸多边形检测),如图:
在这里插入图片描述
可以看出如果点P在凸多边形ABCDE之内,按照逆时针角为正角(顺时针为负角)来看,点P和点ABCDE组成的角度全小于0,反之,则存在大于0的情况,我们来验证一下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestPolygonMathCheck : MonoBehaviour
{
    
    
    public RectTransform[] rectVertices;
    public RectTransform rectPos;

    void Start()
    {
    
    

    }

    void Update()
    {
    
    
        Vector2[] verts = new Vector2[rectVertices.Length];
        Vector2 p = rectPos.anchoredPosition;
        for (int i = 0; i < rectVertices.Length; i++)
        {
    
    
            verts[i] = rectVertices[i].anchoredPosition;
        }
#if UNITY_EDITOR
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector2 a = verts[i];
            Vector2 b;
            if (i < (verts.Length - 1))
            {
    
     
                b = verts[i + 1];
            }
            else
            {
    
    
                b = verts[0];
            }
            Debug.DrawLine(a, b, Color.black);
        }
#endif
        bool result = CheckPosInPolygon(verts, p);
#if UNITY_EDITOR
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector2 a = verts[i];
            Debug.DrawLine(a, p, result ? Color.black : Color.red);
        }
#endif
    }

    private bool CheckPosInPolygon(Vector2[] verts, Vector2 p)
    {
    
    
        Vector2[] cpyverts = new Vector2[verts.Length + 1];
        System.Array.Copy(verts, cpyverts, verts.Length);
        cpyverts[verts.Length] = verts[0];
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector2 a = cpyverts[i];
            Vector2 b = cpyverts[i + 1];
            Vector2 pa = a - p;
            Vector2 pb = b - p;
            float ang = Vector2.SignedAngle(pa, pb);
#if UNITY_EDITOR
            Debug.LogFormat("i = {0} ang = {1}", i, ang);
#endif
            if (ang > 0)
            {
    
    
                return false;
            }
        }
        return true;
    }
}

效果如下:
在这里插入图片描述
当然如果我们增加一个Z轴来看,将二维向量增加到三维,当点P在多边形ABCDE内,看出三维向量PA和PB的叉积向量朝向屏幕之外,也就是Z小于0(Unity是左手坐标系),那么我们再次验证一下:

    private bool CheckPosInPolygon(Vector3[] verts, Vector3 p)
    {
    
    
        Vector3[] cpyverts = new Vector3[verts.Length + 1];
        System.Array.Copy(verts, cpyverts, verts.Length);
        cpyverts[verts.Length] = verts[0];
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 a = cpyverts[i];
            Vector3 b = cpyverts[i + 1];
            Vector3 pa = a - p;
            Vector3 pb = b - p;
            Vector3 pz = Vector3.Cross(pa, pb);
#if UNITY_EDITOR
            Debug.LogFormat("i = {0} pz = {1}", i, pz);
#endif
            if (pz.z > 0)
            {
    
    
                return false;
            }
        }
        return true;
    }

效果如下:
在这里插入图片描述
当然我们要找一个检测速度快一点的,下面做个测试,测试一千万次Angle和Cross计算耗时:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestPolygonCheckQuick : MonoBehaviour
{
    
    
    public RectTransform[] rectVertices;
    public RectTransform rectPos;

    private float realTime = 0f;
    private float elapse = 0f;
    private int calculateTimes = 10000000;

    void Start()
    {
    
    
        //先把CPU调动起来
        for (int i = 0; i < calculateTimes; i++)
        {
    
    
            int a = 1 + 1;
        }
        CheckWhichQuick();
    }

    private void CheckWhichQuick()
    {
    
    

        Vector2[] d2verts = new Vector2[rectVertices.Length];
        Vector2 d2p = rectPos.anchoredPosition;
        for (int i = 0; i < rectVertices.Length; i++)
        {
    
    
            d2verts[i] = rectVertices[i].anchoredPosition;
        }

        Vector3[] d3verts = new Vector3[rectVertices.Length];
        Vector3 d3p = rectPos.anchoredPosition;
        for (int i = 0; i < rectVertices.Length; i++)
        {
    
    
            d3verts[i] = rectVertices[i].anchoredPosition;
        }

        realTime = Time.realtimeSinceStartup;
#if UNITY_EDITOR
        Debug.LogFormat("start check realtime = {0}", realTime);
#endif
        //一千万次angle检测
        for (int i = 0; i < calculateTimes; i++)
        {
    
    
            CheckPosInPolygonByAngle(d2verts, d2p);
        }
#if UNITY_EDITOR
        elapse = Time.realtimeSinceStartup - realTime;
        Debug.LogFormat("angle check time = {0} realtime = {1}", elapse, Time.realtimeSinceStartup);
#endif
        realTime = Time.realtimeSinceStartup;

        //一千万次cross检测
        for (int i = 0; i < calculateTimes; i++)
        {
    
    
            CheckPosInPolygonByCross(d3verts, d3p);
        }
#if UNITY_EDITOR
        elapse = Time.realtimeSinceStartup - realTime;
        Debug.LogFormat("cross check time = {0} realtime = {1}", elapse, Time.realtimeSinceStartup);
#endif
    }

    private void CheckPosInPolygonByCross(Vector3[] verts, Vector3 p)
    {
    
    
        Vector3[] cpyverts = new Vector3[verts.Length + 1];
        System.Array.Copy(verts, cpyverts, verts.Length);
        cpyverts[verts.Length] = verts[0];
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 a = cpyverts[i];
            Vector3 b = cpyverts[i + 1];
            Vector3 pa = a - p;
            Vector3 pb = b - p;
            Vector3 pz = Vector3.Cross(pa, pb);
        }
    }

    private void CheckPosInPolygonByAngle(Vector2[] verts, Vector2 p)
    {
    
    
        Vector2[] cpyverts = new Vector2[verts.Length + 1];
        System.Array.Copy(verts, cpyverts, verts.Length);
        cpyverts[verts.Length] = verts[0];
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector2 a = cpyverts[i];
            Vector2 b = cpyverts[i + 1];
            Vector2 pa = a - p;
            Vector2 pb = b - p;
            float ang = Vector2.SignedAngle(pa, pb);
        }
    }
}

时间结果如下:
在这里插入图片描述
我特意将Cross检测放到前面,结果如下:
在这里插入图片描述
看来怎么都是通过Vector3.Cross检测更快。
但是呢!我们还得接着优化啊,不然怎么叫快速检测?我们检测Polygon之前还得判断点P是否在Polygon的外接矩形中,不然直接开始Polygon检测不得特别消耗CPU资源?因为Rectangle检测更简单,下面开始外接矩形检测优化:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestPolygonRectCheck : MonoBehaviour
{
    
    
    public RectTransform[] rectVertices;
    public RectTransform rectPos;

    private Vector4 rectBound;          //left right botton top

    void Start()
    {
    
    

    }

    private void Update()
    {
    
    
        Vector3[] verts = new Vector3[rectVertices.Length];
        Vector3 p = rectPos.anchoredPosition;
        for (int i = 0; i < rectVertices.Length; i++)
        {
    
    
            verts[i] = rectVertices[i].anchoredPosition;
        }
        rectBound = GetOutsideRectangle(verts);
        bool inrect = CheckPosInRectangle(rectBound, p);
#if UNITY_EDITOR
        Vector2 topleft = new Vector2(rectBound.x, rectBound.w);
        Vector2 topright = new Vector2(rectBound.y, rectBound.w);
        Vector2 bottomleft = new Vector2(rectBound.x, rectBound.z);
        Vector2 bottomright = new Vector2(rectBound.y, rectBound.z);
        Debug.DrawLine(topleft, topright, inrect ? Color.black : Color.blue);
        Debug.DrawLine(topright, bottomright, inrect ? Color.black : Color.blue);
        Debug.DrawLine(bottomright, bottomleft, inrect ? Color.black : Color.blue);
        Debug.DrawLine(bottomleft, topleft, inrect ? Color.black : Color.blue);
#endif
        bool inpolygon = false;
        if (inrect)
        {
    
    
            inpolygon = CheckPosInPolygon(verts, p);
        }
#if UNITY_EDITOR
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 a = verts[i];
            Vector3 b;
            if (i < (verts.Length - 1))
            {
    
    
                b = verts[i + 1];
            }
            else
            {
    
    
                b = verts[0];
            }
            Debug.DrawLine(a, b, inpolygon ? Color.black : Color.blue);
        }
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 a = verts[i];
            Debug.DrawLine(a, p, inpolygon ? Color.black : Color.red);
        }
#endif
    }

    /// <summary>
    /// 计算外接矩形
    /// </summary>
    /// <param name="verts"></param>
    /// <returns></returns>
    private Vector4 GetOutsideRectangle(Vector3[] verts)
    {
    
    
        float left = float.MaxValue;
        float right = float.MinValue;
        float bottom = float.MaxValue;
        float top = float.MinValue;
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 vert = verts[i];
            float x = vert.x;
            float y = vert.y;
            if (left > x)
            {
    
    
                left = x;
            }
            if (right < x)
            {
    
    
                right = x;
            }
            if (bottom > y)
            {
    
    
                bottom = y;
            }
            if (top < y)
            {
    
    
                top = y;
            }
        }
        Vector4 bound = new Vector4(left, right, bottom, top);
        return bound;
    }
    /// <summary>
    /// 检测是否在外接矩形内
    /// </summary>
    /// <param name="bound"></param>
    /// <param name="p"></param>
    /// <returns></returns>
    private bool CheckPosInRectangle(Vector4 bound, Vector3 p)
    {
    
    
        if (p.x < bound.x || p.x > bound.y)
        {
    
    
            return false;
        }
        if (p.y < bound.z || p.y > bound.w)
        {
    
    
            return false;
        }
        return true;
    }
    /// <summary>
    /// 检测是否在多边形内
    /// </summary>
    /// <param name="verts"></param>
    /// <param name="p"></param>
    /// <returns></returns>
    private bool CheckPosInPolygon(Vector3[] verts, Vector3 p)
    {
    
    
        Vector3[] cpyverts = new Vector3[verts.Length + 1];
        System.Array.Copy(verts, cpyverts, verts.Length);
        cpyverts[verts.Length] = verts[0];
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 a = cpyverts[i];
            Vector3 b = cpyverts[i + 1];
            Vector3 pa = a - p;
            Vector3 pb = b - p;
            Vector3 pz = Vector3.Cross(pa, pb);
#if UNITY_EDITOR
            Debug.LogFormat("i = {0} pz = {1}", i, pz);
#endif
            if (pz.z > 0)
            {
    
    
                return false;
            }
        }
        return true;
    }
}

效果如下:
在这里插入图片描述
这样就相当于每次多边形检测都要先进行简单的外接矩形检测,首先得确定点P在外接矩形中,才开始进行多边形检测。
当然了,还没完!如果一个屏幕空间有大量的多边形需要做检测,光是外接矩形判断也可能可能很耗时,我们还能不能优化呢?这时候就要用空间四叉树来做预处理了。
打个比方,将1920*1080屏幕进行四等分(左下角E0、右下角E1、左上角E2、右上角E3),每一分像素区域960x540,那么点P就只会归属于一个E区内(例如在E0区内),同时假设屏幕上所有多边形全部都在E1区内,那么P点和多边形ABCED就八竿子打不着,此时还进行多边形的外接矩形检测是不是毫无意义?如图:
在这里插入图片描述
那么如果我们在多边形ABCED的网格构建的同时计算其所处的屏幕子区域,然后对P点所在屏幕子区域判断,判断P点和多边形ABCED所在的屏幕子区域相同,然后才进行外接矩形判断,则再次优化了多边形检测的速度。
当然我实际工程里是进行的十六叉树的屏幕空间划分,这里我的建议是根据实际需求,比如我的工程里,多边形大小大致在整个屏幕的1/16到1/32之间,我进行十六叉树的子区域判断更加合理
当然处理方式有如下:
1.进行一次四叉树的处理
2.在第一次四叉树的子区域中再次进行四叉树处理
3.直接进行十六叉树处理
如果我们在多边形ABCDE网格构建做了屏幕子空间判断的预处理,则2和3是一样的过程。如果我们只在运行时进行多叉树子区域判断,则1和2好一点点。

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class QuickPolygon : MonoBehaviour
{
    
    
    public RectTransform[] rectVertices;

    [HideInInspector] public Vector3[] rectVerts;                   //顶点(顺时针)
    [HideInInspector] public Vector4 rectBound;                     //外接矩形
    [HideInInspector] public int[] eSubIds;                         //E子区域Ids /*0-15*/

    void Start()
    {
    
    
        BuildParameter();
    }

    void Update()
    {
    
    
#if UNITY_EDITOR
        Vector2 topleft = new Vector2(rectBound.x, rectBound.w);
        Vector2 topright = new Vector2(rectBound.y, rectBound.w);
        Vector2 bottomleft = new Vector2(rectBound.x, rectBound.z);
        Vector2 bottomright = new Vector2(rectBound.y, rectBound.z);
        Debug.DrawLine(topleft, topright, Color.black);
        Debug.DrawLine(topright, bottomright, Color.black);
        Debug.DrawLine(bottomright, bottomleft, Color.black);
        Debug.DrawLine(bottomleft, topleft, Color.black);

        for (int i = 0; i < rectVerts.Length; i++)
        {
    
    
            Vector3 a = rectVerts[i];
            Vector3 b;
            if (i < (rectVerts.Length - 1))
            {
    
    
                b = rectVerts[i + 1];
            }
            else
            {
    
    
                b = rectVerts[0];
            }
            Debug.DrawLine(a, b, Color.blue);
        }
#endif
    }
    /// <summary>
    /// 构建多边形参数
    /// </summary>
    private void BuildParameter()
    {
    
    
        rectVerts = new Vector3[rectVertices.Length];
        for (int i = 0; i < rectVertices.Length; i++)
        {
    
    
            rectVerts[i] = rectVertices[i].anchoredPosition;
        }
        rectBound = GetOutsideRectangle(rectVerts);
        eSubIds = GetMultiTreeBoundsCross(rectBound, Vector2.zero, new Vector2(Screen.width, Screen.height));
#if UNITY_EDITOR
        string lstr = string.Empty;
        for (int i = 0; i < eSubIds.Length; i++)
        {
    
    
            lstr += (eSubIds[i] + " ");
        }
        Debug.LogFormat("exist in subids = {0}", lstr);
#endif
        //在检测管理器中刷新polygon数组
        ScreenPolygonCheck.GetInstance().AddPolygon(this);
    }

    /// <summary>
    /// 给定屏幕空间多叉后判断与bound相交,返回相交区域id
    /// 区域id:
    /// 12 13 14 15
    /// 8  9  10 11
    /// 4  5  6  7
    /// 0  1  2  3
    /// </summary>
    /// <returns></returns>
    private int[] GetMultiTreeBoundsCross(Vector4 bound, Vector2 origin, Vector2 size, int tree = 4)
    {
    
    
        Vector2 dsize = size / tree;
        List<int> sublist = new List<int>();
        for (int y = 0; y < tree; y++)
        {
    
    
            for (int x = 0; x < tree; x++)
            {
    
    
                Vector2 eorg = origin + new Vector2(x * dsize.x, y * dsize.y);
                if (CheckBoundsCross(bound, eorg, dsize))
                {
    
    
                    int id = x + y * tree;
                    sublist.Add(id);
                }
            }
        }
        return sublist.ToArray();
    }
    /// <summary>
    /// 检测bounds相交
    /// </summary>
    /// <returns></returns>
    private bool CheckBoundsCross(Vector4 bound, Vector2 origin, Vector2 size)
    {
    
    
        Vector4 b2 = new Vector4(origin.x, origin.x + size.x, origin.y, origin.y + size.y);
        return CheckBoundsCross(bound, b2);
    }
    /// <summary>
    /// 检测bounds相交
    /// 得到b1的中心c1
    /// 得到b2的中心c2
    /// 如果c1c2的xy轴差值大于半宽之和或者半高之和,就不相交
    /// </summary>
    /// <param name="b1"></param>
    /// <param name="b2"></param>
    /// <returns></returns>
    private bool CheckBoundsCross(Vector4 b1, Vector4 b2)
    {
    
    
        //取b1的半宽 半高 和 中心c1
        float b1hwid = (b1.y - b1.x) / 2f;
        float b1hhei = (b1.w - b1.z) / 2f;
        Vector2 c1 = new Vector2((b1.x + b1.y) / 2f, (b1.z + b1.w) / 2f);
        //取b2的半宽 半高  和 中心c1
        float b2hwid = (b2.y - b2.x) / 2f;
        float b2hhei = (b2.w - b2.z) / 2f;
        Vector2 c2 = new Vector2((b2.x + b2.y) / 2f, (b2.z + b2.w) / 2f);
        //如果c1和c2的x轴距离>b1wid+b2wid 或 y轴距离>b1hei+b2hei,那么b1和b2不相交
        float c1c2x = Mathf.Abs(c1.x - c2.x);
        float c1c2y = Mathf.Abs(c1.y - c2.y);
        if (c1c2x > (b1hwid + b2hwid) || c1c2y > (b1hhei + b2hhei))
        {
    
    
            return false;
        }
        return true;
    }

    /// <summary>
    /// 计算外接矩形
    /// </summary>
    /// <param name="verts"></param>
    /// <returns></returns>
    private Vector4 GetOutsideRectangle(Vector3[] verts)
    {
    
    
        float left = float.MaxValue;
        float right = float.MinValue;
        float bottom = float.MaxValue;
        float top = float.MinValue;
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 vert = verts[i];
            float x = vert.x;
            float y = vert.y;
            if (left > x)
            {
    
    
                left = x;
            }
            if (right < x)
            {
    
    
                right = x;
            }
            if (bottom > y)
            {
    
    
                bottom = y;
            }
            if (top < y)
            {
    
    
                top = y;
            }
        }
        Vector4 bound = new Vector4(left, right, bottom, top);
        return bound;
    }
    /// <summary>
    /// 检测是否在外接矩形内
    /// </summary>
    /// <param name="p"></param>
    /// <returns></returns>
    public bool CheckPosInRectangle(Vector2 p)
    {
    
    
        return CheckPosInRectangle(rectBound, p);
    }
    /// <summary>
    /// 检测是否在外接矩形内
    /// </summary>
    /// <param name="bound"></param>
    /// <param name="p"></param>
    /// <returns></returns>
    public bool CheckPosInRectangle(Vector4 bound, Vector3 p)
    {
    
    
        if (p.x < bound.x || p.x > bound.y)
        {
    
    
            return false;
        }
        if (p.y < bound.z || p.y > bound.w)
        {
    
    
            return false;
        }
        return true;
    }
    /// <summary>
    /// 检测是否在多边形内
    /// </summary>
    /// <param name="p"></param>
    /// <returns></returns>
    public bool CheckPosInPolygon(Vector2 p)
    {
    
    
        return CheckPosInPolygon(rectVerts, p);
    }
    /// <summary>
    /// 检测是否在多边形内
    /// </summary>
    /// <param name="verts"></param>
    /// <param name="p"></param>
    /// <returns></returns>
    public bool CheckPosInPolygon(Vector3[] verts, Vector3 p)
    {
    
    
        Vector3[] cpyverts = new Vector3[verts.Length + 1];
        System.Array.Copy(verts, cpyverts, verts.Length);
        cpyverts[verts.Length] = verts[0];
        for (int i = 0; i < verts.Length; i++)
        {
    
    
            Vector3 a = cpyverts[i];
            Vector3 b = cpyverts[i + 1];
            Vector3 pa = a - p;
            Vector3 pb = b - p;
            Vector3 pz = Vector3.Cross(pa, pb);
#if UNITY_EDITOR
            Debug.LogFormat("i = {0} pz = {1}", i, pz);
#endif
            if (pz.z > 0)
            {
    
    
                return false;
            }
        }
        return true;
    }
}

这里构建一个快速多边形数据类,用于每次多边形网格更新进行数据更新,如下:
在这里插入图片描述
可以看得出来蓝色多边形的黑色外接矩形,占据了1 2 5 6 9 10子区域,而且数据计算正确,那么接下来开始屏幕点击的管理检测:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScreenPolygonCheck : MonoSingleton<ScreenPolygonCheck>
{
    
    
    private Vector2 screenOrigin;
    private Vector2 screenSize;

    private List<QuickPolygon>[] polyListArr = new List<QuickPolygon>[16];           //储存对应subId的PolygonList

    void Start()
    {
    
    
        screenOrigin = Vector2.zero;
        screenSize = new Vector2(Screen.width, Screen.height);
    }

    void Update()
    {
    
    
#if UNITY_EDITOR
        for (int x = 0; x <= 4; x++)
        {
    
    
            Vector2 fbottom = new Vector2(Screen.width * (x / 4f), 0);
            Vector2 ftop = new Vector2(Screen.width * (x / 4f), Screen.height);
            Debug.DrawLine(fbottom, ftop, Color.yellow);
            Vector2 fleft = new Vector2(0, Screen.height * (x / 4f));
            Vector2 fright = new Vector2(Screen.width, Screen.height * (x / 4f));
            Debug.DrawLine(fleft, fright, Color.yellow);
        }
#endif

        if (Input.GetMouseButtonDown(0))
        {
    
    
            Vector2 spos = Input.mousePosition;
            int sid;
            //先判断子区域id
            bool result = GetMultiTreeIndex(out sid, spos, screenOrigin, screenSize);
            if (result)
            {
    
    
                List<QuickPolygon> polylist = polyListArr[sid];
                if (polylist != null && polylist.Count > 0)
                {
    
    
#if UNITY_EDITOR
                    Debug.LogWarningFormat("ScreenPolygonCheck GetMultiTreeIndex sid = {0}", sid);
#endif
                    //再从数组取sid的polylis
                    for (int i = 0; i < polylist.Count; i++)
                    {
    
    
                        QuickPolygon poly = polylist[i];
                        //再判断rectangle
                        if(poly.CheckPosInRectangle(spos))
                        {
    
    
#if UNITY_EDITOR
                            Debug.LogWarningFormat("ScreenPolygonCheck CheckPosInRectangle");
#endif
                            //最后判断polygon
                            if (poly.CheckPosInPolygon(spos))
                            {
    
    
#if UNITY_EDITOR
                                Debug.LogWarningFormat("ScreenPolygonCheck CheckPosInPolygon Final Succeed!!!");
#endif
                            }
                        }
                    }
                }
            }
        }
    }
    /// <summary>
    /// 获取screenPos所在的多叉树的id
    /// </summary>
    /// <param name="spos"></param>
    /// <param name="origin"></param>
    /// <param name="size"></param>
    /// <returns></returns>
    private bool GetMultiTreeIndex(out int sid, Vector2 spos, Vector2 origin, Vector2 size, int tree = 4)
    {
    
    
        Vector2 dsize = size / tree;
        for (int y = 0; y < tree; y++)
        {
    
    
            for (int x = 0; x < tree; x++)
            {
    
    
                Vector2 eorg = origin + new Vector2(x * dsize.x, y * dsize.y);
                if (CheckPosInBound(spos, eorg, dsize))
                {
    
    
                    sid = x + y * tree;
#if UNITY_EDITOR
                    Debug.LogFormat("ScreenPolygonCheck GetMultiTreeIndex succeed sid = {0} spos = {1} origin = {2} size = {3}", sid, spos, origin, size);
#endif
                    return true;
                }
            }
        }
#if UNITY_EDITOR
        Debug.LogErrorFormat("ScreenPolygonCheck GetMultiTreeIndex error spos = {0} origin = {1} size = {2}", spos, origin, size);
#endif
        sid = -1;
        return false;
    }
    /// <summary>
    /// 检测pos在bound内
    /// </summary>
    /// <param name="pos"></param>
    /// <param name="origin"></param>
    /// <param name="size"></param>
    /// <returns></returns>
    private bool CheckPosInBound(Vector2 pos, Vector2 origin, Vector2 size)
    {
    
    
        float left = origin.x;
        float right = origin.x + size.x;
        float bottom = origin.y;
        float top = origin.y + size.y;
        if (pos.x < left || pos.x > right || pos.y < bottom || pos.y > top)
        {
    
    
            return false;
        }
        return true;
    }

    /// <summary>
    /// polygon进行网格重建后,根据SubIds储存到数组
    /// </summary>
    /// <param name="polygon"></param>
    public void AddPolygon(QuickPolygon polygon)
    {
    
    
        RemovePolygon(polygon);
        int[] subids = polygon.eSubIds;
        for (int i = 0; i < subids.Length; i++)
        {
    
    
            int sid = subids[i];
            if (polyListArr[sid] == null)
            {
    
    
                polyListArr[sid] = new List<QuickPolygon>();
            }
            polyListArr[sid].Add(polygon);
        }
    }
    /// <summary>
    /// 移除一个polygon
    /// </summary>
    /// <param name="polygon"></param>
    public void RemovePolygon(QuickPolygon polygon)
    {
    
    
        for (int i = 0; i < 16; i++)
        {
    
    
            List<QuickPolygon> polylist = polyListArr[i];
            if (polylist != null && polylist.Contains(polygon))
            {
    
    
                polylist.Remove(polygon);
            }
        }
    }
}

在检测管理器内,我们根据SID(也就是子区域id)在快速多边形数据构建的时候储存其引用,这个数组的维护是时间离散的,根据多边形自身重建刷新,而不是一股脑都放Update中检测,然后判断鼠标点击的ScreenPosition,获取其SID,再匹配相应的QuickPolygonList,再做外接矩形判断,最后再做多边形判断。其中数据重建是根据QuickPolygon网格重建同时刷新的,所以时间上是离散的,分散了CPU的运行压力,而Update中进行多边形检测,是依次分层的运算压力,直到每一层的条件满足才进行下一层运算,也避免CPU的过度开销。
效果如下:
在这里插入图片描述
可以看出三层检测是ok的。
最后以实际工程进行示例:
在这里插入图片描述
我这七八年前的现在只能用来看看爱奇艺和做做休闲手游电脑开发运行起来也ok,所以性能方面还算满足要求。
ok,以后有时间继续。

猜你喜欢

转载自blog.csdn.net/yinhun2012/article/details/118873924
今日推荐