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