前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unity UI案例(绘制圆环)

Unity UI案例(绘制圆环)

作者头像
bering
发布2019-12-02 13:59:04
1.7K0
发布2019-12-02 13:59:04
举报
文章被收录于专栏:游戏开发之旅游戏开发之旅

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

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

详细设计:

1.饼图的绘制过程:
1.1 获取以某个点为中心,固定半径的圆上的点,再结合原点绘制三角面,可生成扇形;
图一
图一
代码语言:javascript
复制
//计算圆上点的位置 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
代码语言:javascript
复制
    绘制不在是与中心点直接构成三角形面进行绘制了,而是与小尺寸的圆的两个新交点构成了一个矩形,因此得按照两个三角形绘制
代码语言:javascript
复制
2. 文字的添加:
2.1 文字应该显示在折线的左端或者右端,计算出折线的左端或者右端的点的位置,就可以确定文字的位置,并且确定文字应该左对齐,还是右对齐
图3
图3
代码语言:javascript
复制
  //如果需要绘制百分比的文字,则需要我们在绘制扇形的时候记录下扇形中点所对应的弧度,开始绘制的第一个其实弧度加上扇形弧度的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中获取的中点),然后在同一方向的某个距离上取的第二个点,然后在第二个点的同一水平线上取第三个点来绘制折线
代码语言:javascript
复制
  Vector2 secondPoint = new Vector2(Mathf.Cos(middleRadian),Mathf.Sin(middleRadian)) * (radius + brokenLineLenght);
  //第三个点可以根据是在左侧或者是右侧直接水平挪动一定的值即可,比如在右侧
  Vector2 thridPoint = secondPoint + new Vector2(extralenght,0);
  //同理三个点,两两绘制直线即可

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

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

代码语言:javascript
复制
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;
    }
}

调用方法:

代码语言:javascript
复制
  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)
            }));
    }

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

代码语言:javascript
复制
 //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
    }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-12-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 详细设计:
    • 1.饼图的绘制过程:
      • 1.1 获取以某个点为中心,固定半径的圆上的点,再结合原点绘制三角面,可生成扇形;
      • 1.4 实现饼图空心的原理是,绘制时不与原点组合三角面,原理见图2
    • 2. 文字的添加:
      • 2.1 文字应该显示在折线的左端或者右端,计算出折线的左端或者右端的点的位置,就可以确定文字的位置,并且确定文字应该左对齐,还是右对齐
      • 2.2 折线的绘制应该在扇形弧度的中间值处取一点(就是在2.1中获取的中点),然后在同一方向的某个距离上取的第二个点,然后在第二个点的同一水平线上取第三个点来绘制折线
      • 2.3 文字自适应的方法在函数图篇中已经讲解过,方法在代码中也有,不在赘述。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档