码迷,mamicode.com
首页 > 其他好文 > 详细

UGUI圆形UI与多边形点击范围

时间:2021-04-01 13:21:39      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:func   ast   情况   div   oid   ati   说明   pre   inter   

1.前言

Unity默认的ugui mesh是四边形网格,若要实现圆形ui可以使用mask实现,此处提供一种更改mesh的方法,并将点击范围控制在圆形范围内。(从易用性角度并不会方便太多)
同时提供一种在不使用mask的情况下实现复杂点击范围。但此上两种方案均需要判断点击点是否在一个多边形内(圆形按多边形处理)

2.RayCrossing

若判断一个点p是否在一个多边形内,可以使用rayCrossing方法。即点p发出一条射线,与多边形相交,假若交点个数是奇数,说明点p落在多边形内,交点个数为偶数说明点p在多边形外。以点p为端点,水平方向右侧发出的射线来与多边形相交判断,那么顶点v1,v2组成的线段与射线若有交点q,则点q必定满足两个条件:

        v2.y < q.y = p.y > v1.y
        p.x < q.x

方法如下:
如果Contains返回true则包含此点,否则不包含

    private bool Contains(Vector2 p, Vector3[] outterVertices)
    {
        var crossNumber = 0;
        RayCrossing(p, outterVertices, ref crossNumber);//检测内环
        //RayCrossing(p, outterVertices, ref crossNumber);//检测外环
        return (crossNumber & 1) == 1;
    }

    private void RayCrossing(Vector2 p, Vector3[] vertices, ref int crossNumber)
    {
        for (int i = 0, count = vertices.Length; i < count; i++)
        {
            var v1 = vertices[i];
            var v2 = vertices[(i + 1) % count];

            if (((v1.y <= p.y) && (v2.y > p.y))
                || ((v1.y > p.y) && (v2.y <= p.y)))
            {
                if (p.x < v1.x + (p.y - v1.y) / (v2.y - v1.y) * (v2.x - v1.x))
                {
                    crossNumber += 1;
                }
            }
        }
    }

3.圆形UI

示例代码从mesh角度生成圆形mesh,并根据2.0方法将点击范围控制在圆形范围内。示例代码继承RawImage,也可以改成Image或者Graphic或者MaskableGraphic.

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

public class CircleUI : RawImage,IPointerDownHandler,ICanvasRaycastFilter
{
    Vector3[] vertices;
    int[] triangles;
    Vector2[] uvs;

    bool initialed = false;
    float currentRadius = 0;


    protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();

        var size = GetPixelAdjustedRect();
        float radius = (size.width > size.height ? size.height : size.width) / 2;

        if(!initialed || currentRadius != radius)
        {
            vertices = GetVertices(radius);
            triangles = GetTriangles();
            uvs = GetUvs();
        }

        for (int i = 0; i <= triCount; i++)
        {
            vh.AddVert(vertices[i], Color.white, uvs[i]);

            if (i < triCount)
            {
                vh.AddTriangle(triangles[i * 3], triangles[i * 3 + 1], triangles[i * 3 + 2]);
            }
        }
    }

    int triCount = 20;

    protected Vector3[] GetVertices(float radius)
    {
        Vector3 []
        vertices = new Vector3[triCount + 1];
            vertices[0] = Vector3.zero;
            float angleDelta = 2 * Mathf.PI / triCount;

            for (int i = 0; i<triCount; i++)
            {
                float angle = angleDelta * i;
    float x = radius * Mathf.Cos(angle);
    float y = radius * Mathf.Sin(angle);

    vertices[i + 1] = new Vector3(x, y, 0);
}

            return vertices;
    }

    protected int[] GetTriangles()
    {
            int[] triangles = new int[triCount * 3];

            for (int i = 0; i<triCount; i++)
            {

                triangles[i * 3] = 0;
                triangles[i * 3 + 2] = i + 1;

                if (i + 2 > triCount)
                {
                    triangles[i * 3 + 1] = 1;
                }
                else
                {
                    triangles[i * 3 + 1] = i + 2;
                }
            }
            return triangles;
    }

    protected Vector2[] GetUvs()
    {
        Vector2[] uvs = new Vector2[triCount + 1];
        uvs[0] = new Vector2(0.5f, 0.5f);
        float angleDelta = 2 * Mathf.PI / triCount;

        for (int i = 0; i < triCount; i++)
        {
            float angle = angleDelta * i;
            float x = Mathf.Cos(angle) * 0.5f + 0.5f;
            float y = Mathf.Sin(angle) * 0.5f + 0.5f;

            uvs[i + 1] = new Vector2(x, y);
        }
        return uvs;
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        Debug.Log("OnPointerDown");
    }

    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {

        Vector2 local;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, sp, eventCamera, out local);
        return Contains(local, vertices);
    }

    private bool Contains(Vector2 p, Vector3[] outterVertices)
    {
        var crossNumber = 0;
        RayCrossing(p, outterVertices, ref crossNumber);//检测内环
        //RayCrossing(p, outterVertices, ref crossNumber);//检测外环
        return (crossNumber & 1) == 1;
    }

    private void RayCrossing(Vector2 p, Vector3[] vertices, ref int crossNumber)
    {
        for (int i = 0, count = vertices.Length; i < count; i++)
        {
            var v1 = vertices[i];
            var v2 = vertices[(i + 1) % count];

            if (((v1.y <= p.y) && (v2.y > p.y))
                || ((v1.y > p.y) && (v2.y <= p.y)))
            {
                if (p.x < v1.x + (p.y - v1.y) / (v2.y - v1.y) * (v2.x - v1.x))
                {
                    crossNumber += 1;
                }
            }
        }
    }
}

4.多边形点击范围

此示例通过PolygonCollider2D 来实现多边形区域。在需要点击的ui上挂在如下脚本,并挂在PolygonCollider2D 组件,编辑此组件形状以达到需要的点击范围,然后可以通过其points属性获取到点击区域,继而通过判断点是否在点击范围内实现区域点击。由于PolygonCollider2D 组件只是获取点击范围(所以可以修改下面脚本的逻辑,将可点击多边形范围的points的值缓存下来,然后destroy掉PolygonCollider2D 组件)

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

public class PolygonClick : RawImage,ICanvasRaycastFilter
{
    PolygonCollider2D polygon;

    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        Vector2 local;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, sp, eventCamera, out local);
        return Contains(local, polygon.points);
    }

    protected override void Start()
    {
        polygon = GetComponent<PolygonCollider2D>();
    }


    private bool Contains(Vector2 p, Vector2[] outterVertices)
    {
        var crossNumber = 0;
        RayCrossing(p, outterVertices, ref crossNumber);
        return (crossNumber & 1) == 1;
    }

    private void RayCrossing(Vector2 p, Vector2[] vertices, ref int crossNumber)
    {
        for (int i = 0, count = vertices.Length; i < count; i++)
        {
            var v1 = vertices[i];
            var v2 = vertices[(i + 1) % count];

            if (((v1.y <= p.y) && (v2.y > p.y))
                || ((v1.y > p.y) && (v2.y <= p.y)))
            {
                if (p.x < v1.x + (p.y - v1.y) / (v2.y - v1.y) * (v2.x - v1.x))
                {
                    crossNumber += 1;
                }
            }
        }
    }
}

5.结论

UGUI圆形UI与多边形点击范围

标签:func   ast   情况   div   oid   ati   说明   pre   inter   

原文地址:https://www.cnblogs.com/llstart-new0201/p/14603940.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!