Unity引擎绘制多边形属性图

  大家好,我是阿赵
  在制作游戏的时候,经常会遇到需要绘制多边形属性图的需求,比如这种效果:
可以根据需要的属性的数量变化多边形的边数,然后每一个顶点从中心点开始到多边形的顶点的长度代表了该属性的强度,然后多个属性的顶点连接起来形成一个多边形图形。
在这里插入图片描述

  这里介绍一下我自己的做法。

一、 需求拆分:

  需要做到上图的效果,可以拆分成几个部分

1、底板

在这里插入图片描述

  这一个图片,当然也可以用程序计算生成出来,但我个人觉得,这个底图除了会出现边数不一样以外,其他的样式差不多是固定的,而程序生成的图片可能没有美术做的底图好看,所以我是比较建议直接出图使用。如果需要换边数,可以直接换图

2、属性多边形

在这里插入图片描述

  这个形状是会根据属性的变化而变化的,所以是不能直接用美术出图的,必现要动态生成。既然是动态的绘制多边形,所以用到的知识点肯定是动态计算顶点和索引,构成Mesh网格来显示了。由于这个形状基本上都是用于UI的,所以不能直接用Mesh和MeshRenderer来渲染,我这里选择的是MaskableGraphic。
  MaskableGraphic可以直接在UI上通过顶点和索引绘制多边形。所以我们只需要固定一个中心点坐标,然后通过设置一些参数,比如:

  1. 边数
  2. 最大半径
  3. 颜色
  4. 属性的最大最小值
  5. 每一个边的当前值
  6. 等等
      然后根据参数计算几个顶点的坐标,再通过(中心点、当前顶点、下一个顶点)作为一个三角形的索引,就可以逐个三角形绘制出来了。最后指定颜色,半透明也是在颜色里面指定。

3、多边形描边

在这里插入图片描述

  这个的原理和上一步一样,都是使用MaskableGraphic来自定义顶点和索引绘制。
  可以设置的参数和上面绘制多边形基本一致,只需要加多:
1、 描边的厚度
2、 描边的颜色
  绘制描边的算法比绘制多边形本身复杂一些,我一开始想得简单,直接把多边形的半径扩大,然后往回减去描边厚度,来得到描边的一个角上的两个顶点。不过那样做是不行的,将会导致描边的线段不是均匀的厚度,到了尖角的地方会变得很厚。
  后来我还是老老实实的对多边形的每条边进行往内平移,并且求出每条平移后的线和下一条线的延伸线的交点,作为描边的第二层的顶点。然后如果有描边的情况下,多边形的顶点也是使用了描边的第二层的顶点,两者就完全接得上了。
  由于这个原因,所以需要求线段平移和交点的方法。

4、在编辑器调整效果

在这里插入图片描述

  最后,综合以上所述,在编辑器里面可以暴露这些参数,可以在做UI的时候就看到效果,并且用代码去设置。这里还有一个angle角度的变量,是用于旋转多边形的,因为有时候我们的需求多边形不一定是正上方有个顶点,可以是旋转一定角度。
  有一个isChange的变量,下面的代码里面会说到,只有在修改的情况下才会重新计算顶点。所以如果在编辑器的非运行状态下,可以手点一下激活,这样在调整参数的时候,画面就会立刻刷新变化。

二、 代码实现

1、 MaskableGraphic的基础代码

  使用MaskableGraphic之前介绍过,基础用法很简单,类继承MaskableGraphic,然后重写OnPopulateMesh方法,在里面输入顶点和索引列表。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
    [RequireComponent(typeof(CanvasRenderer))]
    [RequireComponent(typeof(RectTransform))]
public class UIPolyOutlineEx : MaskableGraphic
{
        private List<UIVertex> vertexList;
    private List<int> indexList;
        protected override void OnPopulateMesh(VertexHelper vh)
        {
            vh.Clear();           
            if (vertexList != null && vertexList.Count > 0&&indexList!= null && indexList.Count > 0)
            {
                vh.AddUIVertexStream(vertexList, indexList);
            }
      }
}

2、 设置变量

  根据刚才的分析,设置了变量

public int sideCount = 0;
public float radius = 100;
public float minVal = 0.1f;
public float maxVal = 1;
public float edgeLen = 10;
public float[] sideValueArr;
public float angle = 0;
public Color insideColor = Color.green;
public Color edgeColor = Color.black;

public bool isChange = false;
private List<UIVertex> outlinePoints;
private List<UIVertex> insidePoints;
private List<UIVertex> vertexList;
private List<int> indexList;

3、 计算顶点

  需要根据参数,求出内部多边形和外部描边的顶点,中间还有一个平移线段的顶点信息,所以需要三个数组来计算。计算完之后,就把他们保存起来。这里我是直接保存成UIVertex的,由于描边和内部颜色不一样,所以UIVertex里面也记录了颜色,所以描边的顶点列表和内部的顶点列表是完全分开的,就算看着是同一个点,其实也是两边都保存。

    private void CreatePoints()
    {
        List<Vector2> outsidePoints = new List<Vector2>();
        List<Vector2> outsidePoints2 = new List<Vector2>();
        List<Vector2> moveLinePoints = new List<Vector2>();
        outlinePoints = new List<UIVertex>();
        insidePoints = new List<UIVertex>();
        if (sideCount < 3)
        {
            return;
        }

        float offsetRate = edgeLen / radius;
        Rect rect = gameObject.GetComponent<RectTransform>().rect;
        UIVertex vex0 = UIVertex.simpleVert;
        vex0.position = new Vector3(rect.center.x, rect.center.y, 0);
        Vector3 uv0 = new Vector2(0.5f, 0.5f);
        vex0.uv0 = uv0;
        vex0.color = insideColor;
        insidePoints.Add(vex0);
        for (int i = 0; i < sideCount; i++)
        {
            float val = 0;
            if (sideValueArr != null && i < sideValueArr.Length)
            {
                val = sideValueArr[i];
            }
            else
            {
                val = 0;
            }
            float ang = 360f / sideCount * i + angle;
            ang = ang * Mathf.Deg2Rad;
            val = Mathf.Clamp(val, minVal, maxVal);
            float x = val * Mathf.Sin(ang);
            float y = val * Mathf.Cos(ang);
            outsidePoints.Add(new Vector2(x, y));
        }
        if (edgeLen <= 0)
        {
            for (int i = 0; i < outsidePoints.Count; i++)
            {
                UIVertex vex = UIVertex.simpleVert;
                vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);
                Vector2 uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);
                uv *= 0.5f;
                uv += new Vector2(0.5f, 0.5f);
                vex.uv0 = uv;
                vex.color = insideColor;
                insidePoints.Add(vex);
            }
        }
        else
        {
            for (int i = 0; i < outsidePoints.Count; i++)
            {
                Vector2 p0 = Vector2.zero;
                Vector2 p1 = outsidePoints[i];
                Vector2 p2;

                if (i < outsidePoints.Count - 1)
                {
                    p2 = outsidePoints[i + 1];
                }
                else
                {
                    p2 = outsidePoints[0];
                }

                Vector2 foot = GetPointToLine(p0, p1, p2);
                Vector2 dir = p0 - foot;
                dir.Normalize();
                Vector2 p3 = p1 + dir * offsetRate;
                Vector2 p4 = p2 + dir * offsetRate;
                moveLinePoints.Add(p3);
                moveLinePoints.Add(p4);
            }

            for (int i = 0; i < outsidePoints.Count; i++)
            {
                int ind1 = i * 2;
                int ind2 = i * 2 + 1;
                int ind3 = i * 2 - 2;
                int ind4 = i * 2 - 1;
                if (i == 0)
                {
                    ind3 = moveLinePoints.Count - 2;
                    ind4 = moveLinePoints.Count - 1;
                }
                Vector2 crossPoint = GetLineCrossPoint(moveLinePoints[ind1], moveLinePoints[ind2], moveLinePoints[ind3], moveLinePoints[ind4]);
                print(moveLinePoints[ind1] * radius + "," + moveLinePoints[ind2] * radius + "|" + moveLinePoints[ind3] * radius + "," + moveLinePoints[ind4] * radius + "||" + crossPoint * radius);
                outsidePoints2.Add(crossPoint);
            }

            for (int i = 0; i < outsidePoints.Count; i++)
            {

                UIVertex vex = UIVertex.simpleVert;
                vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);
                Vector2 uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);
                uv *= 0.5f;
                uv += new Vector2(0.5f, 0.5f);
                vex.uv0 = uv;
                vex.color = insideColor;
                insidePoints.Add(vex);

                vex = UIVertex.simpleVert;
                vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);
                uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);
                uv *= 0.5f;
                uv += new Vector2(0.5f, 0.5f);
                vex.uv0 = uv;
                vex.color = edgeColor;
                outlinePoints.Add(vex);

                vex = UIVertex.simpleVert;
                vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);
                uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);
                uv *= 0.5f;
                uv += new Vector2(0.5f, 0.5f);
                vex.uv0 = uv;
                vex.color = edgeColor;
                outlinePoints.Add(vex);
            }

        }
    }

    private Vector2 GetPointToLine(Vector2 p0, Vector2 p1, Vector2 p2)
    {
        Vector2 lineDir = p1 - p2;
        lineDir.Normalize();
        float x0 = p0.x;
        float y0 = p0.y;
        float x1 = p1.x;// -lineDir.x*10;
        float y1 = p1.y;// - lineDir.y * 10;
        float x2 = p2.x;// + lineDir.x * 10;
        float y2 = p2.y;// + lineDir.x * 10;
        if ((x1 == x0 && y1 == y0) || (x2 == x0 && y2 == y0))
        {
            return new Vector2(x0, y0);//点和线段一边重合
        }

        float k = 1;
        if (x1 != x2)
        {
            k = (y2 - y1) / (x2 - x1);
        }
        float a = k;
        float b = -1;
        float c = y1 - k * x1;
        float d = Mathf.Abs(a * x0 + b * y0 + c) / Mathf.Sqrt(a * a + b * b);
        float px = (b * b * x0 - a * b * y0 - a * c) / (a * a + b * b);
        float py = (a * a * y0 - a * b * x0 - b * c) / (a * a + b * b);
        return new Vector2(px, py);
    }

    private Vector2 GetLineCrossPoint(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4)
    {
        Vector2 dir1 = (p1 - p2).normalized;
        p1 += dir1;
        p2 -= dir1;
        Vector2 dir2 = (p3 - p4).normalized;
        p3 += dir2;
        p4 -= dir2;
        Vector2 bx = p4 - p3;
        float d1 = Mathf.Abs(CrossMulVec(bx, p1 - p3));
        float d2 = Mathf.Abs(CrossMulVec(bx, p2 - p3));
        float dx = d1 + d2;
        if (dx == 0)
        {
            return p1;
        }
        float t = d1 / dx;
        Vector2 temp = (p2 - p1) * t;
        return p1 + temp;
    }

    private float CrossMulVec(Vector2 p1, Vector2 p2)
    {
        return p1.x * p2.y - p2.x * p1.y;
}

4、 求构成多边形的点和索引

  由于绘制的时候,需要把描边和内部的所有顶点和索引合并在一起,所以这里需要再计算一次,把两个列表合并。

        private void CreateMeshParam()
        {
            vertexList = new List<UIVertex>();
            indexList = new List<int>();
            int insidePointCount = 0;
            if (insidePoints != null && insidePoints.Count > 0)
            {
                insidePointCount = insidePoints.Count;
                for (int i = 0; i < insidePoints.Count; i++)
                {
                    vertexList.Add(insidePoints[i]);
                }
                List<int> tempList = new List<int>();
                if (sideCount > 2)
                {
                    int ind3 = 0;
                    for (int i = 0; i < sideCount; i++)
                    {
                        tempList.Add(0);
                        tempList.Add(i + 1);
                        ind3 = i + 2;
                        if (ind3 > sideCount)
                        {
                            ind3 = 1;
                        }
                        tempList.Add(ind3);
                    }
                }
                indexList = tempList;
            }
            if (outlinePoints != null && outlinePoints.Count > 0)
            {
                for (int i = 0; i < outlinePoints.Count; i++)
                {
                    vertexList.Add(outlinePoints[i]);
                }
                for (int i = 0; i < sideCount; i++)
                {
                    int ind1 = i * 2;
                    int ind2 = i * 2 + 2;
                    int ind3 = i * 2 + 1;
                    int ind4 = i * 2 + 3;
                    if (i == sideCount - 1)
                    {
                        ind2 = 0;
                        ind4 = 1;
                    }
                    indexList.Add(ind1 + insidePointCount);
                    indexList.Add(ind2 + insidePointCount);
                    indexList.Add(ind3 + insidePointCount);
                    indexList.Add(ind3 + insidePointCount);
                    indexList.Add(ind2 + insidePointCount);
                    indexList.Add(ind4 + insidePointCount);
                }
            }
  }

5、 各种属性的GetSet

  在运行的时候,我们不会希望每一帧都需要重复去计算多边形的顶点和索引,希望只有参数变化的时候才重新计算。所以那些可以改变的参数,应该都做成GetSet方法,然后在Set方法的时候,把一个isChange的标记设置为true。那么当isChange为true的时候,才重新计算。

public void InitData(int sideCount,float r,float edge)
{
    SetSideCount(sideCount);
    radius = r;
    edgeLen = edge;
    isChange = true;
}
public void SetIsChange()
{
    isChange = true;
}
public void SetSideCount(int val)
{
    sideCount = val;
    float[] newArr = new float[val];
    if(sideValueArr!=null&&sideValueArr.Length>0)
    {
        for(int i = 0;i<val;i++)
        {
            if(sideValueArr.Length>val)
            {
                newArr[i] = sideValueArr[i];
            }
        }
    }
    sideValueArr = newArr;
    isChange = true;
}

public void SetValArr(float[] vals)
{
    if (sideValueArr == null)
    {
        return;
    }
    for (int i = 0; i < sideValueArr.Length; i++)
    {
        if (i < vals.Length)
        {
            sideValueArr[i] = vals[i];
        }
    }
    isChange = true;
}


public void SetOneVal(int index,float val)
{
    if(index<0)
    {
        return;
    }
    if(sideValueArr != null&&sideValueArr.Length> index)
    {
        sideValueArr[index] = val;
    }
    isChange = true;
}

public void SetOneValByLua(double index, double val)
{
    int ind = Mathf.FloorToInt((float)index);
    float value = (float)val;
    SetOneVal(ind - 1, value);
}

public void SetAngle(float ang)
{
    angle = ang;
    isChange = true;
}

public void SetRange(float min,float max)
{
    minVal = min;
    maxVal = max;
    isChange = true;
}

public void SetInsideColor(Color col)
{
    insideColor = col;
    isChange = true;
}

public void SetOutlineColor(Color col)
{
    edgeColor = col;
    isChange = true;
}


public void SetRadius(float val)
{
    radius = val;
    isChange = true;
}

  这是一个重新计算顶点和索引的方法,只有在isChange为true的时候,才会调用。

private void UpdatePoly()
{
    if (sideCount == 0)
    {
        vertexList = null;
        return;
    }
    CreatePoints();
    CreateMeshParam();
}

6、 修改OnPopulateMesh方法

  在OnPopulateMesh里面判断isChange,然后调用UpdatePoly方法。

   protected override void OnPopulateMesh(VertexHelper vh)
    {
        //base.OnPopulateMesh(vh);
        vh.Clear();
        if(isChange)
        {
            UpdatePoly();
            if(Application.isPlaying)
                isChange = false;
        }
        
        if (vertexList != null && vertexList.Count > 0)
        {
            vh.AddUIVertexStream(vertexList, indexList);
        }
    }

三、完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace azhao
{
    [RequireComponent(typeof(CanvasRenderer))]
    [RequireComponent(typeof(RectTransform))]
    public class UIPolyOutlineEx : MaskableGraphic
    {


        public int sideCount = 0;
        public float radius = 100;
        public float minVal = 0.1f;
        public float maxVal = 1;
        public float edgeLen = 10;
        public float[] sideValueArr;
        public float angle = 0;
        public Color insideColor = Color.green;
        public Color edgeColor = Color.black;

        public bool isChange = false;
        private List<UIVertex> outlinePoints;
        private List<UIVertex> insidePoints;
        private List<UIVertex> vertexList;
        private List<int> indexList;
        // Start is called before the first frame update
        private new IEnumerator Start()
        {
            UpdatePoly();
            yield return null;
        }

        public void InitData(int sideCount,float r,float edge)
        {
            SetSideCount(sideCount);
            radius = r;
            edgeLen = edge;
            isChange = true;
        }
        public void SetIsChange()
        {
            isChange = true;
        }
        public void SetSideCount(int val)
        {
            sideCount = val;
            float[] newArr = new float[val];
            if(sideValueArr!=null&&sideValueArr.Length>0)
            {
                for(int i = 0;i<val;i++)
                {
                    if(sideValueArr.Length>val)
                    {
                        newArr[i] = sideValueArr[i];
                    }
                }
            }
            sideValueArr = newArr;
            isChange = true;
        }

        public void SetValArr(float[] vals)
        {
            if (sideValueArr == null)
            {
                return;
            }
            for (int i = 0; i < sideValueArr.Length; i++)
            {
                if (i < vals.Length)
                {
                    sideValueArr[i] = vals[i];
                }
            }
            isChange = true;
        }


        public void SetOneVal(int index,float val)
        {
            if(index<0)
            {
                return;
            }
            if(sideValueArr != null&&sideValueArr.Length> index)
            {
                sideValueArr[index] = val;
            }
            isChange = true;
        }

        public void SetOneValByLua(double index, double val)
        {
            int ind = Mathf.FloorToInt((float)index);
            float value = (float)val;
            SetOneVal(ind - 1, value);
        }

        public void SetAngle(float ang)
        {
            angle = ang;
            isChange = true;
        }

        public void SetRange(float min,float max)
        {
            minVal = min;
            maxVal = max;
            isChange = true;
        }

        public void SetInsideColor(Color col)
        {
            insideColor = col;
            isChange = true;
        }

        public void SetOutlineColor(Color col)
        {
            edgeColor = col;
            isChange = true;
        }


        public void SetRadius(float val)
        {
            radius = val;
            isChange = true;
        }

        private void UpdatePoly()
        {
            if (sideCount == 0)
            {
                vertexList = null;
                return;
            }
            CreatePoints();
            CreateMeshParam();
        }



        private void CreatePoints()
        {
            List<Vector2> outsidePoints = new List<Vector2>();
            List<Vector2> outsidePoints2 = new List<Vector2>();
            List<Vector2> moveLinePoints = new List<Vector2>();
            outlinePoints = new List<UIVertex>();
            insidePoints = new List<UIVertex>();
            if (sideCount < 3)
            {
                return;
            }

            float offsetRate = edgeLen / radius;
            Rect rect = gameObject.GetComponent<RectTransform>().rect;
            UIVertex vex0 = UIVertex.simpleVert;
            vex0.position = new Vector3(rect.center.x, rect.center.y, 0);
            Vector3 uv0 = new Vector2(0.5f, 0.5f);
            vex0.uv0 = uv0;
            vex0.color = insideColor;
            insidePoints.Add(vex0);
            for (int i = 0; i < sideCount; i++)
            {
                float val = 0;
                if (sideValueArr != null && i < sideValueArr.Length)
                {
                    val = sideValueArr[i];
                }
                else
                {
                    val = 0;
                }
                float ang = 360f / sideCount * i + angle;
                ang = ang * Mathf.Deg2Rad;
                val = Mathf.Clamp(val, minVal, maxVal);
                float x = val * Mathf.Sin(ang);
                float y = val * Mathf.Cos(ang);
                outsidePoints.Add(new Vector2(x, y));
            }
            if (edgeLen <= 0)
            {
                for (int i = 0; i < outsidePoints.Count; i++)
                {
                    UIVertex vex = UIVertex.simpleVert;
                    vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);
                    Vector2 uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);
                    uv *= 0.5f;
                    uv += new Vector2(0.5f, 0.5f);
                    vex.uv0 = uv;
                    vex.color = insideColor;
                    insidePoints.Add(vex);
                }
            }
            else
            {
                for (int i = 0; i < outsidePoints.Count; i++)
                {
                    Vector2 p0 = Vector2.zero;
                    Vector2 p1 = outsidePoints[i];
                    Vector2 p2;

                    if (i < outsidePoints.Count - 1)
                    {
                        p2 = outsidePoints[i + 1];
                    }
                    else
                    {
                        p2 = outsidePoints[0];
                    }

                    Vector2 foot = GetPointToLine(p0, p1, p2);
                    Vector2 dir = p0 - foot;
                    dir.Normalize();
                    Vector2 p3 = p1 + dir * offsetRate;
                    Vector2 p4 = p2 + dir * offsetRate;
                    moveLinePoints.Add(p3);
                    moveLinePoints.Add(p4);
                }

                for (int i = 0; i < outsidePoints.Count; i++)
                {
                    int ind1 = i * 2;
                    int ind2 = i * 2 + 1;
                    int ind3 = i * 2 - 2;
                    int ind4 = i * 2 - 1;
                    if (i == 0)
                    {
                        ind3 = moveLinePoints.Count - 2;
                        ind4 = moveLinePoints.Count - 1;
                    }
                    Vector2 crossPoint = GetLineCrossPoint(moveLinePoints[ind1], moveLinePoints[ind2], moveLinePoints[ind3], moveLinePoints[ind4]);
                    print(moveLinePoints[ind1] * radius + "," + moveLinePoints[ind2] * radius + "|" + moveLinePoints[ind3] * radius + "," + moveLinePoints[ind4] * radius + "||" + crossPoint * radius);
                    outsidePoints2.Add(crossPoint);
                }

                for (int i = 0; i < outsidePoints.Count; i++)
                {

                    UIVertex vex = UIVertex.simpleVert;
                    vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);
                    Vector2 uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);
                    uv *= 0.5f;
                    uv += new Vector2(0.5f, 0.5f);
                    vex.uv0 = uv;
                    vex.color = insideColor;
                    insidePoints.Add(vex);

                    vex = UIVertex.simpleVert;
                    vex.position = new Vector3(rect.center.x + outsidePoints[i].x * radius, rect.center.y + outsidePoints[i].y * radius, 0);
                    uv = new Vector2(outsidePoints[i].x, outsidePoints[i].y);
                    uv *= 0.5f;
                    uv += new Vector2(0.5f, 0.5f);
                    vex.uv0 = uv;
                    vex.color = edgeColor;
                    outlinePoints.Add(vex);

                    vex = UIVertex.simpleVert;
                    vex.position = new Vector3(rect.center.x + outsidePoints2[i].x * radius, rect.center.y + outsidePoints2[i].y * radius, 0);
                    uv = new Vector2(outsidePoints2[i].x, outsidePoints2[i].y);
                    uv *= 0.5f;
                    uv += new Vector2(0.5f, 0.5f);
                    vex.uv0 = uv;
                    vex.color = edgeColor;
                    outlinePoints.Add(vex);
                }

            }
        }

        private Vector2 GetPointToLine(Vector2 p0, Vector2 p1, Vector2 p2)
        {
            Vector2 lineDir = p1 - p2;
            lineDir.Normalize();
            float x0 = p0.x;
            float y0 = p0.y;
            float x1 = p1.x;// -lineDir.x*10;
            float y1 = p1.y;// - lineDir.y * 10;
            float x2 = p2.x;// + lineDir.x * 10;
            float y2 = p2.y;// + lineDir.x * 10;
            if ((x1 == x0 && y1 == y0) || (x2 == x0 && y2 == y0))
            {
                return new Vector2(x0, y0);//点和线段一边重合
            }

            float k = 1;
            if (x1 != x2)
            {
                k = (y2 - y1) / (x2 - x1);
            }
            float a = k;
            float b = -1;
            float c = y1 - k * x1;
            float d = Mathf.Abs(a * x0 + b * y0 + c) / Mathf.Sqrt(a * a + b * b);
            float px = (b * b * x0 - a * b * y0 - a * c) / (a * a + b * b);
            float py = (a * a * y0 - a * b * x0 - b * c) / (a * a + b * b);
            return new Vector2(px, py);
        }

        private Vector2 GetLineCrossPoint(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4)
        {
            Vector2 dir1 = (p1 - p2).normalized;
            p1 += dir1;
            p2 -= dir1;
            Vector2 dir2 = (p3 - p4).normalized;
            p3 += dir2;
            p4 -= dir2;
            Vector2 bx = p4 - p3;
            float d1 = Mathf.Abs(CrossMulVec(bx, p1 - p3));
            float d2 = Mathf.Abs(CrossMulVec(bx, p2 - p3));
            float dx = d1 + d2;
            if (dx == 0)
            {
                return p1;
            }
            float t = d1 / dx;
            Vector2 temp = (p2 - p1) * t;
            return p1 + temp;
        }

        private float CrossMulVec(Vector2 p1, Vector2 p2)
        {
            return p1.x * p2.y - p2.x * p1.y;
        }

        private void CreateMeshParam()
        {
            vertexList = new List<UIVertex>();
            indexList = new List<int>();
            int insidePointCount = 0;
            if (insidePoints != null && insidePoints.Count > 0)
            {
                insidePointCount = insidePoints.Count;
                for (int i = 0; i < insidePoints.Count; i++)
                {
                    vertexList.Add(insidePoints[i]);
                }
                List<int> tempList = new List<int>();
                if (sideCount > 2)
                {
                    int ind3 = 0;
                    for (int i = 0; i < sideCount; i++)
                    {
                        tempList.Add(0);
                        tempList.Add(i + 1);
                        ind3 = i + 2;
                        if (ind3 > sideCount)
                        {
                            ind3 = 1;
                        }
                        tempList.Add(ind3);
                    }
                }
                indexList = tempList;
            }
            if (outlinePoints != null && outlinePoints.Count > 0)
            {
                for (int i = 0; i < outlinePoints.Count; i++)
                {
                    vertexList.Add(outlinePoints[i]);
                }
                for (int i = 0; i < sideCount; i++)
                {
                    int ind1 = i * 2;
                    int ind2 = i * 2 + 2;
                    int ind3 = i * 2 + 1;
                    int ind4 = i * 2 + 3;
                    if (i == sideCount - 1)
                    {
                        ind2 = 0;
                        ind4 = 1;
                    }
                    indexList.Add(ind1 + insidePointCount);
                    indexList.Add(ind2 + insidePointCount);
                    indexList.Add(ind3 + insidePointCount);
                    indexList.Add(ind3 + insidePointCount);
                    indexList.Add(ind2 + insidePointCount);
                    indexList.Add(ind4 + insidePointCount);
                }
            }
        }

        // Update is called once per frame
        void Update()
        {

        }


        protected override void OnPopulateMesh(VertexHelper vh)
        {
            //base.OnPopulateMesh(vh);
            vh.Clear();
            if(isChange)
            {
                UpdatePoly();
                if(Application.isPlaying)
                    isChange = false;
            }
            
            if (vertexList != null && vertexList.Count > 0)
            {
                vh.AddUIVertexStream(vertexList, indexList);
            }
        }
    }
}
### 回答1: Unity是一款强大的游戏引擎,拥有丰富的绘图功能。要绘制一个多边形三角网,我们可以通过以下步骤来实现。 首先,在Unity的场景中创建一个空物体作为基准对象,用来放置绘制多边形。然后,我们需要定义多边形的顶点坐标。可以使用Vector3类型的数组来表示顶点坐标。 接下来,我们需要使用Unity的Mesh组件来创建三角网格。可以通过Mesh的vertices属性来设置顶点坐标。然后,通过添加三角形的顶点索引来定义每个三角形的形状。可以使用Mesh的triangles属性来设置三角形的顶点索引。 为了正确绘制多边形三角网,我们还需要设置Mesh的uv属性和normals属性。uv属性用于设置纹理坐标,而normals属性用于设置顶点的法线方向。 最后,将创建的Mesh添加到基准物体上的MeshFilter组件中,并将基准物体添加到场景中。 完成上述步骤后,我们就成功绘制了一个多边形三角网。可以通过调整顶点坐标和三角形的顶点索引,来实现不同形状的多边形。 需要注意的是,Unity绘制多边形三角网是以三角形为基本单元的,因此无论输入的多边形是几边形,最终都会被分解成由三角形组成的形状来绘制。 总结起来,通过在Unity中创建空物体,定义顶点坐标,设置三角形的顶点索引,设置纹理坐标和法线方向,以及将Mesh添加到基准物体上的MeshFilter组件中,就可以实现绘制多边形三角网。 ### 回答2: Unity 是一款流行的游戏引擎,它具有强大的绘图功能,可以用来绘制多边形三角网。 在 Unity 中,我们可以使用 Mesh 类来创建和绘制多边形三角网。首先,我们需要创建一个 Mesh 对象,然后设置它的顶点坐标和三角面的顶点索引。 例如,我们可以创建一个正方形的三角网。首先,我们定义该正方形的顶点坐标,一般按照顺时针或逆时针的顺序定义。然后,我们设置三角面的顶点索引,这里每三个顶点定义一个三角面。最后,我们将顶点坐标和三角面的顶点索引赋值给 Mesh 对象,并将其设置为游戏对象的 MeshFilter 组件的 mesh 属性。 在运行时,Unity 会根据这些定义好的顶点和三角面来绘制三角网。我们可以给三角网添加材质,调整颜色和纹理等属性,使其更加生动和丰富。 除了手动创建和设置顶点和三角面,Unity 还提供了一些简化的方法来绘制常见的形状,如正方形、圆形等。我们可以使用这些方法来快速创建并绘制多边形三角网。 总之,Unity 作为一款强大的游戏引擎,可以通过 Mesh 类来绘制多边形三角网。我们可以手动定义顶点坐标和三角面的顶点索引,也可以使用 Unity 提供的简化方法来创建和绘制多边形三角网。这样,我们就可以在游戏中实现各种各样的多边形形状,并赋予它们丰富的材质和纹理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值