在Unity中使用uGUI绘制自定义图形(饼状图 雷达图)

饼状图或者是雷达图是根据属性自动生成的自定义图形。这里展示了如何使用uGUI完成这一功能。

先附上我制作雷达图的控件的代码  UIPropWidget.cs

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;

/*
 *
 *          2 6
 *
 *   3                 7
 *
 *      0   1 5    4
 *
 *
 * 2 6位为属性0    3为属性1    0为属性2     4为属性3     7为属性4
*/

public class UIPropWidget : Graphic
{
    private enum AnimationStatus
    {
        NOT_START,
        ANIMATING,
        FINISH,
    }
    public List<Vector2> _maxPropVector;
    public List<int> _testProp;
    public bool _withAnimation = true;

    private const int VERTEX_SIZE = 8;                  // 必须为4的倍数  通过绘制两个四边形组成一个五边形
    private const float ANIMATION_TIME = 0.8f;
    private const float MAX_PROP_VALUE = 100.0f;

    private List<Vector2> _propList = new List<Vector2>();
    private List<Vector2> _currentList = new List<Vector2>();

    private List<UIVertex> _vertexList = new List<UIVertex>();

    private bool _isStartAnimation = false;
    private bool _isAnimationFinish = false;
    private bool _isSetValue = false;

    private float _startTime = 0;
    private float _currentTime = 0;

    protected void Awake()
    {
        _isStartAnimation = false;
        _isAnimationFinish = false;
        _isSetValue = false;

        for (int i = 0; i < VERTEX_SIZE; ++i) {
            _propList.Add(Vector2.zero);
            _currentList.Add(Vector2.zero);
        }
    }

    // 设置五个属性值
    public void SetPropList(List<int> list, bool withAnimation = false)
    {
        if (list.Count < 5) {
            Log.Error("必须提供5个属性");
            return;
        }

        // 给每个属性顶点赋值
        _propList[0] = (_maxPropVector[0] - Vector2.zero) * list[2] / MAX_PROP_VALUE;
        _propList[2] = (_maxPropVector[2] - Vector2.zero) * list[0] / MAX_PROP_VALUE;
        _propList[3] = (_maxPropVector[3] - Vector2.zero) * list[1] / MAX_PROP_VALUE;
        _propList[4] = (_maxPropVector[4] - Vector2.zero) * list[3] / MAX_PROP_VALUE;
        _propList[6] = (_maxPropVector[6] - Vector2.zero) * list[0] / MAX_PROP_VALUE;
        _propList[7] = (_maxPropVector[7] - Vector2.zero) * list[4] / MAX_PROP_VALUE;

        // 1 5值是一样的,根据0 4位置连线取中点获取
        _propList[1] = (_propList[0] + _propList[4]) / 2;
        _propList[5] = (_propList[0] + _propList[4]) / 2;

        _isSetValue = true;

        if (withAnimation) {
            PlayAnimation();
        } else {
            for (int i = 0; i < VERTEX_SIZE; ++i) {
                _currentList[i] = _propList[i];
            }
        }

        SetVerticesDirty();
    }

    // 开始播放动画
    public void PlayAnimation()
    {
        _isAnimationFinish = false;
        _isStartAnimation = true;
        _startTime = Time.time;
    }

    void Update()
    {
        if (_isAnimationFinish || !_isSetValue || !_isStartAnimation) {
            return;
        }

        // 动画播放完毕
        if (Time.time - _startTime >= ANIMATION_TIME) {
            for (int i = 0; i < VERTEX_SIZE; ++i) {
                _currentList[i] = _propList[i];
            }

            _isAnimationFinish = true;
            return;
        }

        // 更新当前动画的数据
        float percent = (Time.time - _startTime) / ANIMATION_TIME;
        for (int i = 0; i < VERTEX_SIZE; ++i) {
            _currentList[i] = _propList[i] * percent;
        }

        SetVerticesDirty();
    }

    private void UpdateVertex(List<UIVertex> vbo, List<Vector2> list)
    {
        // 必须要保证填充的是4的倍数
        for (int i = 0; i < VERTEX_SIZE; ++i) {
            var vert = UIVertex.simpleVert;
            vert.color = color;
            if (i < list.Count) {
                vert.position = list[i];
            } else {
                vert.position = list[list.Count - 1];
            }
            vbo.Add(vert);
        }
    }

    protected override void OnFillVBO(List<UIVertex> vbo)
    {
        // 尚未赋值,不用绘制
        if (!_isSetValue) {
            return;
        }

        UpdateVertex(vbo, _currentList);
    }
}

先要说明一下uGUI的渲染体系。 简单来说,就是一个CanvasRenderer进行绘制,所有的控件和可显示的元素都是Graphic。Graphic持有一个canvasRenderer,通过SetVertices设置顶点,最终完成绘制。 举例来说,Image控件就是一个Graphic,这个GameObject上面同时还有一个CanvasRenderer,两者结合起来最终把图片绘制完成。

设置的顶点格式是UIVertex,包含position、normal、color、uv0等属性。最关键的就是position,一般传一个点的坐标是相对于它自己的坐标系的像素坐标,不是全局坐标,也不是相对于父节点的坐标。举例来说,一张100*100的图片,锚点为(0.5,0.5),那么它的四个UIVertex的值分别为 (-50, -50)  (-50, 50)  (50, 50)  (50, -50)。 无论如何移动它的位置或者改变屏幕分辨率,这几个值是不变的。除非改变Image的大小。

还有一个需要注意的是,SetVertices中设置的顶点数目必须是4的倍数,因为uGUI的绘制元素是Quad而不是三角形,所以我绘制一个五边形的雷达图的时候,需要8个顶点,通过两个四边形组合成一个五边形。

最后补充一些关于vertex设置的知识点。

一个控件的GameObject上面只允许有一个Graphic,所以不可能同时存在Image和Text。 我们自定义形状的控件可以通过两种方式来实现,一种是重载Graphic,这样这个控件就与Image等价,这里有两个比较重要的可以重载的函数 UpdateGeometry和OnFillVBO。如果看下uGUI的源代码可以发现,UpdateGeometry其实就是获取一个List<UIVertex>,调用OnFillVBO设置顶点数据,再调用所有的BaseVertexEffect组件进行顶点修改,最后传递给canvasRenderer。
OnFillVBO就是我们常用的设置顶点的地方,只要在里面给vbo的参数Add数据就可以了,重复一下上文说过的,Add的数目必须是4的倍数。 Image和Text都是通过这里设置顶点数据的。

上面有提到BaseVertexEffect,这个就是另外一个可以修改顶点信息的地方,它是一个修饰的组件,以Text和Outline为例,Text是一个Graphic,在控件上面添加的Outline

就是一个BaseVertexEffect,Graphic在运行的时候会获取控件上面所有的BaseVertexEffect,然后设置顶点的时候依次调用。  我们可以实现一个自定义效果,继承自BaseVertexEffect,然后重载ModifyVertex函数进行顶点设置。

当这些知识点理清楚后,一个雷达图简直是小菜一碟。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-01 22:44:18

在Unity中使用uGUI绘制自定义图形(饼状图 雷达图)的相关文章

Android中使用ListView绘制自定义表格(2)

上回再写了<Android中使用ListView绘制自定义表格>后,很多人留言代码不全和没有数据样例.但因为项目原因,没法把源码全部贴上来.近两天,抽空简化了一下,做了一个例子. 效果图如 一.功能: 1.支持列合并 2.考虑了界面刷新优化 3.预留部分接口 4.支持左右滚动 1.枚举类:CellTypeEnum package csdn.danielinbiti.custometableview.item; public enum CellTypeEnum { STRING //字符 ,DI

Android中使用ListView绘制自定义表格(3)

把自定义表格又改进了一下,可以支持行合并.表格分为简单和复杂两种模式 1.简单模式就是<Android中使用ListView绘制自定义表格(2)>描述的方式.不支持行合并 2.复杂模式支持行列合并 1.基于上回上传的代码,改动文件如下 package csdn.danielinbiti.custometableview.item; public class ItemCell { private String cellValue = "";//单元格的值 private in

使用Ogre::ManualObject 绘制自定义图形

在ogre中如果需要进行自定义图形绘制可以使用ManualObject.例如绘制一个三角形的用法如下: SceneNode* pGridNode = m_pBaseNode->createChildSceneNode("gridNode"); //创建ManualObject Ogre::ManualObject* grid = m_pSceneMgr->createManualObject("gird"); //创建材质 //使用ogre的自带材质 g

使用UGUI绘制自定义几何图形

本文展示了如何使用UGUI绘制矩形,同理可绘制其他几何图形. UGUI的渲染体系,简单来说所有的控件和可显示的元素都是Graphic.Graphic持有一个CanvasRenderer,通过SetVertices设置顶点,最终完成绘制. 举例来说,Image控件就是一个Graphic,这个GameObject上面同时还有一个CanvasRenderer,两者结合起来最终把图片绘制完成. 重点在于绘制函数修改为OnPopulateMesh,使用VertexHelper来设置顶点,先设置所有的顶点,

unity中计算ugui中Text控件的所需长度

背景是这样的,我在unity中需要在我按下某个按键的时候,然后显示出被射线碰撞到的物体的信息.物体的信息之前已经整理好成txt文本了.读取一下就可以了.那么这时候就有一个问题,因为每个物体的介绍信息不是一样的长度,那么如果当text控件里面文字的长度超过显示的长度时,超过部分就不再显示.那么这该怎么办. 我是通过去便利txt文本中的所有文字和符号,然后将所有换行符也就是/n的个数记录下来,然后计算一下对应字体大小一行有几个字,比如我的是18号字体,那么一行可以写30个字,然后计算一下txt文本中

JavaScript+svg绘制的一个饼状图

svg参考:https://www.w3.org/TR/SVG/ <body onload='document.body.appendChild( pieChart([12,23,34,45],640,400,200,200,150, ["red","blue","yellow","green"], ["North","South","East","W

UIBezierPath+CAShapeLayer 绘制自定义图形

转载注明出处,谢谢. 使用CAShapeLayer与UIBezierPath可以实现不在view的drawRect方法中就画出一些想要的图形. 1.背景知识: UIBezierPath:UIBezierPath是在UIKit中的一个类,继承于NSObject,可以创建基于矢量的路径.此类是Core Graphics框架关于path的一个OC封装.使用此类可以定义常见的圆形.多边形等形状.我们使用直线.弧(arc)来创建复杂的曲线形状.每一个直线段或者曲线段的结束的地方是下一个的开始的地方.每一个

Unity中为UGUI中的Image置空

首先需要一开始获取一张空的图片,然后进行记录,当想要给一个Image中的图片置空是时候就可以将其中的值置为其: public class ImageChange : MonoBehaviour { //获取的是原来的空图片 private Sprite FormatSprite; public Sprite ChangeSprite; private void Start() { FormatSprite = transform.GetComponent<Image>().sprite; }

C# 绘制PDF图形——基本图形、自定义图形、色彩透明度

引言 在PDF中我们可以通过C#程序代码来添加非常丰富的元素来呈现我们想要表达的内容,如绘制表格.文字,添加图形.图像等等.在本篇文章中,我将介绍如何在PDF中绘制图形,并设置图形属性的操作. 文章中将分以下要点进行介绍: 1. 绘制基本图形(线条.椭圆.圆形.矩形.三角形) 2. 绘制自定义图形 3. 绘制图形并设置图形透明度 所需工具:Spire.PDF for .NET 4.0 提示:安装后,直接引用安装路径下Bin文件夹中的dll文件到项目程序中即可. [示例1]绘制基本图形 C# 步骤