Unity UI案例(绘制圆环)

  • 2019 年 12 月 2 日
  • 笔记

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/CJB_King/article/details/78861038

详细设计:

1.饼图的绘制过程:

1.1 获取以某个点为中心,固定半径的圆上的点,再结合原点绘制三角面,可生成扇形;
//计算圆上点的位置 Smooth代表圆的光滑程度,也就是饼图被分为多少等份    int Smooth = 100;    float perRadian = Matfh.PI * 2 / Smooth;//得到每份所占弧度    然后根据某一块饼图所占的比例计算出它在圆周上相交的点,我们规定从饼图右侧中间位置开始,逆时针方向计算。见图1:      //比如绘制19%比例的扇形图    float radius = x;//半径    float startRadian = 0;    Vector2 startPoint = new Vector2( radius,0 );    for( int i = 0 ; i * perRadian < 0.19f * Mathf.PI * 2 ; i ++ )    {        float endRadian = startRadian + perRadian;        Vector2 endPoint = new Vector2(Matfh.Cos(startRadian),Mathf.Sin(endRadian));        startRadian = endRadian;        startPoint = endPoint;        //如上我们就可以利用startPoint点和endPoint和原点绘制出一个三角形,多个三角形的"积分"就是扇形了    }
1.4 实现饼图空心的原理是,绘制时不与原点组合三角面,原理见图2
    绘制不在是与中心点直接构成三角形面进行绘制了,而是与小尺寸的圆的两个新交点构成了一个矩形,因此得按照两个三角形绘制

2. 文字的添加:

2.1 文字应该显示在折线的左端或者右端,计算出折线的左端或者右端的点的位置,就可以确定文字的位置,并且确定文字应该左对齐,还是右对齐
  //如果需要绘制百分比的文字,则需要我们在绘制扇形的时候记录下扇形中点所对应的弧度,开始绘制的第一个其实弧度加上扇形弧度的1/2    float middleRadian = startRadian + radian / 2.0f;    Vector2 point = new Vector2(Mathf.Cos(middleRadian),Mathf.Sin(middleRadian)) * radius;    //这时我们还需要记录下它应该靠左显示还是靠右显示,所有相对于中心点(0,0)为正在右,反则在左。

2.2 折线的绘制应该在扇形弧度的中间值处取一点(就是在2.1中获取的中点),然后在同一方向的某个距离上取的第二个点,然后在第二个点的同一水平线上取第三个点来绘制折线
  Vector2 secondPoint = new Vector2(Mathf.Cos(middleRadian),Mathf.Sin(middleRadian)) * (radius + brokenLineLenght);    //第三个点可以根据是在左侧或者是右侧直接水平挪动一定的值即可,比如在右侧    Vector2 thridPoint = secondPoint + new Vector2(extralenght,0);    //同理三个点,两两绘制直线即可

2.3 文字自适应的方法在函数图篇中已经讲解过,方法在代码中也有,不在赘述。

绘制圆的代码如下(有注释):

public class PieData  {      public float Percent;      public Color colorF;      public PieData() { }      public PieData(float percent,Color color0)      {          Percent = percent/100.0f;          colorF = color0;      }      public PieData(float percent)      {          Percent = percent / 100.0f;          colorF = Color.white * percent;      }  }    public class Pies  {      public IList<PieData> PieDatas = new List<PieData>();      public Pies() { }      public Pies(IList<PieData> pieDatas)      {          PieDatas = pieDatas;      }      public void AddPieData(Pies pies)      {          foreach (PieData pieData in pies.PieDatas)              PieDatas.Add(pieData);      }  }    public class PipeGraphic : MaskableGraphic {      private  Pies pie = new Pies();      //private List<PieText> pieText = null;      [Range(0, 15)]public float BoomDegree = 1.5f;      [Range(20, 100)]public float Smooth = 100;      [Header("Percent Text Setting")]      public bool IsShowPercnet = true;      [Range(20,150)]      public float holoWidth = 25;      [Range(3,100)]      public float radius = 3;      public void Inject(IList<float> percents,IList<Color> colors)      {          IList<PieData> piesdatas = new List<PieData>();          for(int i=0;i<percents.Count;i++)          {              piesdatas.Add(new PieData(percents[i],colors[i]));          }          Inject(piesdatas);      }      public void Inject(IList<PieData> pieData)      {          Inject(new Pies(pieData));      }      public void Inject(Pies pies)      {          pie.AddPieData(pies);      }          protected override void OnPopulateMesh(VertexHelper vh)      {          if (pie == null) return;          vh.Clear();          vh.AddUIVertexTriangleStream(DrawPie());      }      private List<UIVertex> DrawPie()      {          if (pie == null || pie.PieDatas.Count <= 0)              return new List<UIVertex>();          //if (IsShowPercnet)          //pieText = new List<PieText>();          List<UIVertex> vertexs = new List<UIVertex>();          float perRadian = Mathf.PI * 2 / Smooth; //Smooth个圆弧,每一个所占比例          float totalRadian = 0;          float boomRadian = BoomDegree * Mathf.PI / 180;  //间隙所占圆的百分比          float pieRadianBase =Mathf.PI*2- boomRadian*pie.PieDatas.Count; //要绘制圆的总比例=2π-间隙所占百分比          for(int i=0;i<pie.PieDatas.Count;i++)          {              PieData data = pie.PieDatas[i];              float endRadian = totalRadian + pieRadianBase * data.Percent + boomRadian; //当前总的弧长+数据所占弧长+间隙弧长=一个数据的最大弧长              for(float r=totalRadian+boomRadian;r<endRadian;r+=perRadian)              {                  //这里r是弧度对应圆心角度                  Vector2 first = new Vector2(Mathf.Cos(r),Mathf.Sin(r))*holoWidth;  //从圆的右边开始绘制(第一个点坐标在圆心的右边)                  Vector2 second = new Vector2(Mathf.Cos(r+perRadian),Mathf.Sin(r+perRadian))*holoWidth;                  Vector2 third = new Vector2(Mathf.Cos(r),Mathf.Sin(r))*radius;  //半径*(角度的余弦,正弦)即是横纵坐标值                  Vector2 four = new Vector2(Mathf.Cos(r+perRadian),Mathf.Sin(r+perRadian))*radius;                  vertexs.Add(GetUIVertex(first,data.colorF));                  vertexs.Add(GetUIVertex(second,data.colorF));                  vertexs.Add(GetUIVertex(third, data.colorF));                  vertexs.Add(GetUIVertex(third,data.colorF));                  vertexs.Add(GetUIVertex(second, data.colorF));                  vertexs.Add(GetUIVertex(four,data.colorF));//(四个顶点)两个三角形拼接成一个矩形,坐标点顺序需要注意              }                totalRadian = endRadian;          }          return vertexs;      }      private UIVertex GetUIVertex(Vector3 point,Color dataColor)      {          UIVertex vertex = new UIVertex          {              position = point,              color = dataColor,              uv0 = new Vector2(0, 0)          };          return vertex;      }  }

调用方法:

  public PipeGraphic pipeGraph;      void Start()      {          pipeGraph.Inject(new Pies(new List<PieData>()              {                  new PieData(25 ,Color.blue),                  new PieData(25 ,Color.red),                  new PieData(25 ,Color.green),                  new PieData(25 ,Color.yellow)              }));      }

接下来是绘制饼状数据图完整代码,调用方法同上:

 //Pie Base Data ,use this data to build Pie Graph      [System.Serializable]      public class PieData      {          public float Percent;          public Color Color;          public PieData(){}          public PieData(float percent,Color color0)          {              Percent = percent / 100.0f;              Color = color0;          }            public PieData(float percent)          {              Percent = percent / 100.0f;              // auto set color by percent              Color = Color.white * Percent;          }      };        //Use this data struct to draw a Pie Graph      [System.Serializable]      public class Pies      {          public IList<PieData> PieDatas = new List<PieData>();          public Pies(){}          public Pies( IList<PieData> pieDatas )          {              PieDatas = pieDatas;          }            public void AddPieData( Pies piedatas )          {              foreach (PieData pieData in piedatas.PieDatas )                  PieDatas.Add(pieData);          }      };        //GUI Text information for pie Graph      [System.Serializable]      public class PieText      {          public string Content = null;          public Vector2 Position;          public bool IsLeft = true;          public PieText(){}            public PieText( string content,Vector2 position ,bool isLeft )          {              Content = content;              Position = position;              IsLeft = isLeft;          }      }        // core class      public class PieGraph : MaskableGraphic      {          [Header("Pie Base Setting")]          [Range(5 , 150)]public float PieRadius   = 60.0f;          [Range(0 , 120)]public float HollowWidth = 0.0f;          [Range(0, 15)] public float BoomDegree = 1.5f;          [Range(20, 200)] public float Smooth = 100;            [Header("Percent Text Setting")]          public bool IsShowPercnet = true;          [Range(0.5f, 4)] public float BrokenLineWidth = 2;            private Pies PieData = new Pies();          private List<PieText> _pieText = null;          private Vector3 _realPosition;            public void Inject( IList<float> percents,IList<Color> colors )          {              IList<PieData> piedatas= new List<PieData>();              for ( int i = 0 ; i < percents.Count ; i++ )                  piedatas.Add(new PieData(percents[i] , colors[i]));              Inject(piedatas);          }          public void Inject( IList<float> percents )          {              IList<PieData> piedatas = new List<PieData>();              foreach (float percent in percents)                  piedatas.Add(new PieData(percent));              Inject(piedatas);          }          public void Inject( IList<PieData> pieData )          {              Inject(new Pies(pieData));          }          public void Inject( Pies pies )          {              PieData.AddPieData(pies);          }            #region draw pie            private void OnGUI()          {              if ( null == _pieText ) return;              if (!IsShowPercnet) return;              Vector3 result = transform.localPosition;              _realPosition = getScreenPosition(transform , ref result);              foreach ( PieText text in _pieText )              {                  Vector2 position = local2Screen(_realPosition , text.Position);                  GUIStyle guiStyle = new GUIStyle();                  guiStyle.normal.textColor = Color.black;                  guiStyle.fontStyle = FontStyle.Bold;                  guiStyle.alignment = TextAnchor.MiddleLeft;                  if ( !text.IsLeft )                      guiStyle.alignment = TextAnchor.MiddleRight;                  if(text.IsLeft)                      position += new Vector2(3,-10);                  else                      position += new Vector2(-23,-10);                  GUI.Label(new Rect(position , new Vector2(20 , 20)) , text.Content , guiStyle);              }          }            //draw pie core method          protected override void OnPopulateMesh(VertexHelper vh)          {              if ( null == PieData ) return;              vh.Clear();              vh.AddUIVertexTriangleStream(DrawPie());          }          private List<UIVertex> DrawPie()          {              if (PieData == null || PieData.PieDatas.Count <= 0)                  return new List<UIVertex>();              if (IsShowPercnet)                  _pieText = new List<PieText>();              List<UIVertex> vertexs = new List<UIVertex>();              float perRadian = Mathf.PI * 2 / Smooth;              float totalRadian = 0;              float boomRadian = BoomDegree * Mathf.PI / 180 ;              float pieRadianBase = Mathf.PI * 2 - boomRadian * PieData.PieDatas.Count;              for ( int i = 0 ; i < PieData.PieDatas.Count ; i++ )              {                  PieData data = PieData.PieDatas[i];                  float endRadian = boomRadian + data.Percent * pieRadianBase + totalRadian;                  for ( float r = boomRadian + totalRadian ; r < endRadian ; r += perRadian )                  {                      Vector2 first = new Vector2(Mathf.Cos(r) , Mathf.Sin(r)) * HollowWidth;                      Vector2 second = new Vector2(Mathf.Cos(r + perRadian) , Mathf.Sin(r + perRadian)) * HollowWidth;                      Vector2 third = new Vector2(Mathf.Cos(r) , Mathf.Sin(r)) * PieRadius;                      Vector2 four = new Vector2(Mathf.Cos(r + perRadian) , Mathf.Sin(r + perRadian)) * PieRadius;                      vertexs.Add(GetUIVertex(first , data.Color));                      vertexs.Add(GetUIVertex(third , data.Color));                      vertexs.Add(GetUIVertex(second , data.Color));                      vertexs.Add(GetUIVertex(second , data.Color));                      vertexs.Add(GetUIVertex(third , data.Color));                      vertexs.Add(GetUIVertex(four , data.Color));                  }                    #region ShowPercnet                  if (IsShowPercnet)                  {                      float middleRadian = boomRadian + data.Percent * pieRadianBase / 2 + totalRadian;                      float brokenLineLength = PieRadius * 0.2f;                      Vector2 middlePoint = new Vector2(Mathf.Cos(middleRadian), Mathf.Sin(middleRadian)) * PieRadius;                      Vector2 secondPoint = middlePoint + new Vector2(Mathf.Cos(middleRadian), Mathf.Sin(middleRadian)) * brokenLineLength;                      Vector2 first = middlePoint + new Vector2(Mathf.Sin(middleRadian), -Mathf.Cos(middleRadian)) * BrokenLineWidth / 2;                      Vector2 second = middlePoint + new Vector2(-Mathf.Sin(middleRadian), Mathf.Cos(middleRadian)) * BrokenLineWidth / 2;                      Vector2 third = secondPoint + new Vector2(Mathf.Sin(middleRadian), -Mathf.Cos(middleRadian)) * BrokenLineWidth / 2;                      Vector2 four = secondPoint + new Vector2(-Mathf.Sin(middleRadian), Mathf.Cos(middleRadian)) * BrokenLineWidth / 2;                      Vector2 five;                      Vector2 six;                        if (middleRadian > Mathf.PI / 2 && middleRadian < Mathf.PI * 3 / 2)                      {                          five = third + new Vector2(-brokenLineLength, 0);                          six = four + new Vector2(-brokenLineLength, 0);                          // right text                          _pieText.Add(new PieText(data.Percent * 100 + "%", six, false));                      }                      else                      {                          five = third + new Vector2(brokenLineLength, 0);                          six = four + new Vector2(brokenLineLength, 0);                          // left text                          _pieText.Add(new PieText(data.Percent * 100 + "%", six, true));                      }                        vertexs.Add(GetUIVertex(first, data.Color));                      vertexs.Add(GetUIVertex(second, data.Color));                      vertexs.Add(GetUIVertex(third, data.Color));                        vertexs.Add(GetUIVertex(third, data.Color));                      vertexs.Add(GetUIVertex(second, data.Color));                      vertexs.Add(GetUIVertex(four, data.Color));                        vertexs.Add(GetUIVertex(third, data.Color));                      vertexs.Add(GetUIVertex(four, data.Color));                      vertexs.Add(GetUIVertex(five, data.Color));                        vertexs.Add(GetUIVertex(five, data.Color));                      vertexs.Add(GetUIVertex(four, data.Color));                      vertexs.Add(GetUIVertex(six, data.Color));                  }                  #endregion                  totalRadian = endRadian;              }              return vertexs;          }            #endregion            #region draw helper methods            private UIVertex GetUIVertex( Vector2 point , Color color0 )          {              UIVertex vertex = new UIVertex              {                  position = point ,                  color = color0 ,                  uv0 = new Vector2(0 , 0)              };              return vertex;          }            private Vector2 local2Screen( Vector2 parentPos,Vector2 localPosition )          {              Vector2 pos = localPosition + parentPos;              float xValue, yValue = 0;              if ( pos.x > 0 )                  xValue = pos.x + Screen.width / 2.0f;              else                  xValue = Screen.width / 2.0f - Mathf.Abs(pos.x);              if ( pos.y > 0 )                  yValue = Screen.height / 2.0f - pos.y;              else                  yValue = Screen.height / 2.0f + Mathf.Abs(pos.y);              return new Vector2(xValue,yValue);          }            private Vector2 getScreenPosition( Transform trans, ref Vector3 result )          {              if ( null != trans.parent && null != trans.parent.parent )              {                  result += trans.parent.localPosition;                  getScreenPosition(trans.parent , ref result);              }              if ( null != trans.parent && null == trans.parent.parent )                  return result;              return result;          }            #endregion      }