Ilingis 发表于 2023-1-10 20:51

Unity UGUI新手引导镂空遮罩效果

效果图:


原理实现:
重写Graphic类的OnPopulateMesh方法
接下来我们需要明白一点就是unity画出的模型都是由N多三角面组合而成
接下来我们先看个示例,借助OnPopulateMesh画个三角形


using UnityEngine.UI;
using UnityEngine;

public class Test : Graphic
{
    protected override void OnPopulateMesh(VertexHelper vh)
    {
      base.OnPopulateMesh(vh);

      vh.Clear();
      vh.AddVert(new Vector3(0, 0), Color.black, Vector2.zero); //添加顶点
      vh.AddVert(new Vector3(0, 100), Color.black, Vector2.zero);
      vh.AddVert(new Vector3(100, 0), Color.black, Vector2.zero);

      vh.AddTriangle(0, 1, 2); //依据三个顶点组成三角面
    }
}最后我们为了实现最上面的效果,先画出外边形,然后画圆角(注意圆角实际上由N多个三角形组成,当三角形分的越多圆角越平滑)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;

public class Test : Graphic
{
    public float Radius = 10f;//内切圆半径 图片的一半差不多就是一个圆了 这里相当于图片十分之一的长度
    public int TriangleNum = 6;//每个扇形三角形个数 个数越大弧度越平滑

    public RectTransform inner_trans;
    private RectTransform outer_trans;//背景区域

    private Vector2 inner_rt;//镂空区域的右上角坐标
    private Vector2 inner_lb;//镂空区域的左下角坐标
    private Vector2 outer_rt;//背景区域的右上角坐标
    private Vector2 outer_lb;//背景区域的左下角坐标
   
   
    public bool realtimeRefresh;
   
   
    public bool ShowHollowOut = true;
    protected override void Awake()
    {
      base.Awake();

      outer_trans = GetComponent<RectTransform>();

      //计算边界
      CalcBounds();
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
      vh.Clear();
      float tw = Mathf.Abs(inner_lb.x - inner_rt.x);//图片的宽
      float th = Mathf.Abs(inner_lb.y - inner_rt.y);//图片的高
      float twr = tw / 2;
      float thr = th / 2;

      if (Radius < 0)
            Radius = 0;
      float radius = tw / Radius;//半径这里需要动态计算确保不会被拉伸
      if (radius > twr)
            radius = twr;
      if (radius < 0)
            radius = 0;
      if (TriangleNum <= 0)
            TriangleNum = 1;

      UIVertex vert = UIVertex.simpleVert;
      vert.color = color;

      //0 outer左下角
      vert.position = new Vector2(outer_lb.x, outer_lb.y);
      vh.AddVert(vert);
      //1 outer左上角
      vert.position = new Vector2(outer_lb.x, outer_rt.y);
      vh.AddVert(vert);
      //2 outer右上角
      vert.position = new Vector2(outer_rt.x, outer_rt.y);
      vh.AddVert(vert);
      //3 outer右下角
      vert.position = new Vector2(outer_rt.x, outer_lb.y);
      vh.AddVert(vert);

      //4 inner左下角
      vert.position = new Vector3(inner_lb.x, inner_lb.y);
      vh.AddVert(vert);
      //5 inner左上角
      vert.position = new Vector3(inner_lb.x, inner_rt.y);
      vh.AddVert(vert);
      //6 inner右上角
      vert.position = new Vector3(inner_rt.x, inner_rt.y);
      vh.AddVert(vert);
      //7 inner右下角
      vert.position = new Vector3(inner_rt.x, inner_lb.y);
      vh.AddVert(vert);

      //不显示镂空
      if (ShowHollowOut == false)
      {
            vh.AddTriangle(0, 1, 2);
            vh.AddTriangle(2, 3, 0);
            return;
      }
      //绘制三角形
      vh.AddTriangle(0, 1, 4);
      vh.AddTriangle(1, 4, 5);
      vh.AddTriangle(1, 5, 2);
      vh.AddTriangle(2, 5, 6);
      vh.AddTriangle(2, 6, 3);
      vh.AddTriangle(6, 3, 7);
      vh.AddTriangle(4, 7, 3);
      vh.AddTriangle(0, 4, 3);


      //内四边形顶点
      List<Vector2> commonPointOutside = new List<Vector2>();//共点列表
      Vector2 point1 = new Vector2(inner_lb.x, inner_lb.y);//左下共点
      Vector2 point2 = new Vector2(inner_lb.x, inner_lb.y + radius);//决定首次旋转方向的点
      commonPointOutside.Add(point1);
      commonPointOutside.Add(point2);
      point1 = new Vector2(inner_lb.x, inner_rt.y);//左上共点
      point2 = new Vector2(inner_lb.x + radius, inner_rt.y);
      commonPointOutside.Add(point1);
      commonPointOutside.Add(point2);
      point1 = new Vector2(inner_rt.x, inner_rt.y);//右上共点
      point2 = new Vector2(inner_rt.x, inner_rt.y - radius);
      commonPointOutside.Add(point1);
      commonPointOutside.Add(point2);
      point1 = new Vector2(inner_rt.x, inner_lb.y);//右下共点
      point2 = new Vector2(inner_rt.x - radius, inner_lb.y);
      commonPointOutside.Add(point1);
      commonPointOutside.Add(point2);


      //圆心公共点
      List<Vector2> commonPoint = new List<Vector2>();//共点列表
      point1 = new Vector2(inner_lb.x + radius, inner_lb.y + radius);//左下共点
      point2 = new Vector2(inner_lb.x, inner_lb.y + radius);//决定首次旋转方向的点
      commonPoint.Add(point1);
      commonPoint.Add(point2);
      point1 = new Vector2(inner_lb.x + radius, inner_rt.y - radius);//左上共点
      point2 = new Vector2(inner_lb.x + radius, inner_rt.y);
      commonPoint.Add(point1);
      commonPoint.Add(point2);
      point1 = new Vector2(inner_rt.x - radius, inner_rt.y - radius);//右上共点
      point2 = new Vector2(inner_rt.x, inner_rt.y - radius);
      commonPoint.Add(point1);
      commonPoint.Add(point2);
      point1 = new Vector2(inner_rt.x - radius, inner_lb.y + radius);//右下共点
      point2 = new Vector2(inner_rt.x - radius, inner_lb.y);
      commonPoint.Add(point1);
      commonPoint.Add(point2);

      Vector2 pos2;


      float degreeDelta = (float)(Mathf.PI / 2 / TriangleNum);//每一份等腰三角形的角度 默认6份
      List<float> degreeDeltaList = new List<float>() { Mathf.PI, Mathf.PI / 2, 0, (float)3 / 2 * Mathf.PI };

      for (int j = 0; j < commonPoint.Count; j += 2)
      {
            float curDegree = degreeDeltaList;//当前的角度
            AddVert(commonPointOutside, tw, th, vh, vert);//添加扇形区域所有三角形公共顶点//TODO :这里改成外面的点
            int thrdIndex = vh.currentVertCount;//当前三角形第二顶点索引
            int TriangleVertIndex = vh.currentVertCount - 1;//一个扇形保持不变的顶点索引
            List<Vector2> pos2List = new List<Vector2>();
            for (int i = 0; i < TriangleNum; i++)
            {
                curDegree += degreeDelta;
                if (pos2List.Count == 0)
                {
                  AddVert(commonPoint, tw, th, vh, vert);
                }
                else
                {
                  vert.position = pos2List;
                  vert.uv0 = new Vector2(pos2List.x + 0.5f, pos2List.y + 0.5f);
                }
                pos2 = new Vector2(commonPoint.x + radius * Mathf.Cos(curDegree), commonPoint.y + radius * Mathf.Sin(curDegree));
                AddVert(pos2, tw, th, vh, vert);
                vh.AddTriangle(TriangleVertIndex, thrdIndex, thrdIndex + 1);
                thrdIndex++;
                pos2List.Add(vert.position);
            }
      }
    }
    protected Vector2[] GetTextureUVS(Vector2[] vhs, float tw, float th)
    {
      int count = vhs.Length;
      Vector2[] uvs = new Vector2;
      for (int i = 0; i < uvs.Length; i++)
      {
            uvs = new Vector2(vhs.x / tw + 0.5f, vhs.y / th + 0.5f);//矩形的uv坐标因为uv坐标原点在左下角,vh坐标原点在中心 所以这里加0.5(uv取值范围0~1)
      }
      return uvs;
    }
    protected void AddVert(Vector2 pos0, float tw, float th, VertexHelper vh, UIVertex vert)
    {
      vert.position = pos0;
      vert.uv0 = GetTextureUVS(new[] { new Vector2(pos0.x, pos0.y) }, tw, th);
      vh.AddVert(vert);
    }

    /// <summary>
    /// 计算边界
    /// </summary>
    private void CalcBounds()
    {
      if (inner_trans == null)
      {
            return;
      }

      Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(outer_trans, inner_trans);
      inner_rt = bounds.max;
      inner_lb = bounds.min;
      outer_rt = outer_trans.rect.max;
      outer_lb = outer_trans.rect.min;
    }

    private void Update()
    {
      if (realtimeRefresh == false)
      {
            return;
      }

      //计算边界
      CalcBounds();
      //刷新
      SetAllDirty();
    }
}

工程下载
页: [1]
查看完整版本: Unity UGUI新手引导镂空遮罩效果