Unity雷达图制作

雷达图制作

一、前言

之前面试的时候,有面试官问了我关于怎么根据数据去制作UI雷达图的,当时没有回答出来,问了室友说可以用Mesh网格去实现,这两天又在公司看到了有关于这类相关的代码,所以自己想实现一下,文章仅限参考.

二、思路

MaskableGraphic:UGUI的核心组件,它继承自Graphic.MashableGraphic是一个抽象类,它的派生类有RawImage,Image, Text,顾名思义,MaskableGraphic是可遮罩的图像.
VertexHelper :VertexHelper是UI绘制的一个帮助类,绘制之前需要先调用vh.Clear(),将原有的网格UI清空.

  • 流程思路
  1. 创建类:写一个类于Image组件的类,继承于MaskableGraphic类
  2. 重绘图形:在类中继承MaskableGraphicd.OnPoPulateMesh(VertexHelper vh)函数.
  3. 添加顶点和图形:使用VertexHelper中的AddVert(添加顶点)或AddUIVerttexQuad(创建四边形)函数等,绘制图形
  • 实现思路
  1. 度数(degree): 根据传入的数据的数量去平分一个圆的360°,得到每个数据的度数 360 / Count(数据数量)
  2. 顶点数(vertSize):由于要将图形首尾连接起来,最后一个顶点要与第一个顶点相同,所以顶点数要比数据量大1 即Count + 1.
  3. 长度(pos1):即顶点离中心点的距离.dist * sin dist * cos
  • 了解UGUI相关的API

根据官网去了解相关的API,本文章会讲解一下需要用的API.

  1. Graphic:UI.Graphic - Unity 脚本 API
  2. VertexHelper:UI.VertexHelper - Unity 脚本 API
  3. UIVertex:UnityEngine.UIVertex - Unity 脚本 API

三、Graphic 的部分API

3.1 描述

所有视觉UI组件的基类,创建视觉UI组件时,应该继承此类.

3.2 API

公共函数描述
OnPopulateMesh一个回调函数,当UI元素需要生成顶点数据时,会调用这个函数
SetAllDirty将布局设为"脏"
SetVerticesDirty将顶点标记为"脏"
SetNativeSize调整图形大小使其像素精确

四、VertexHelper的部分API

4.1 描述

可辅助UI生成网格的Utility类,此类实现IDisposable,用以辅助内存管理,个人理解,这是一个类似于写入流的类,不过这个写入的是网格数据.

4.2 API

变量描述注释
currentIndexCount获取在VertexHelper中设置的索引数索引数:流中被利用的数
currentVertCount缓冲区中当前的顶点数顶点数:顶点个数
公共函数描述API
AddTriangle向缓冲区添加一个三角形public void AddTriangle(int idx1, int idx2, int idx3)
AddUIVerttexQuad向流中添加一个四边形public void AddUIVerttexQuad(UIVertex[] verts)
AddVert向流中添加一个顶点AddVert(UIVertex v), Add(Vector3 position, Color32 color, Vector2 uv0)
FillMesh使用流数据填充指定网格public void FillMesh(Mesh mesh)

五、UIVertex

5.1 描述

Canvas用于管理顶点的类

5.2 API

变量描述
simpleVert静态变量,简单UIVertex
color顶点颜色
normal法线
position顶点位置
tangent切线
uv0网格下第一个纹理坐标集

六、项目实现

6.1 具体表现

该项目根据LPL数据去制作的,LPL冲呀,The Shy冲呀
在这里插入图片描述
在这里插入图片描述

6.2 代码

  • UI_Radar类
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UI_Radar : MaskableGraphic
{
    //public Texture texture;
    private float size = 0;
    private int sides = 6;
    //顶点距离
    public List<float> vertDist = new List<float>(6);
    private float rotation = 3.148f;
    protected override void Start()
    {
        vertDist.Clear();
        if (vertDist == null) vertDist = new List<float>();
        if(vertDist.Count != 0)
        {
            for (int i = 0; i < vertDist.Count; i++)
            {
                vertDist[i] = 1f;
            }
        }
    }

    private void Update()
    {
        //根据当前物体的宽高去适配尺寸
        size = rectTransform.rect.width;
        if (rectTransform.rect.width > rectTransform.rect.height)
            size = rectTransform.rect.height;
        else
            size = rectTransform.rect.width;
    }

    private bool ConvertData(HeroData hero, out List<float>temp )
    {
        temp = new List<float>();
        temp.Add(hero.averageDamage);
        temp.Add(hero.damageConversion);
        temp.Add(hero.damagepercentage);
        temp.Add(hero.fieldAverageKill);
        temp.Add(hero.Viability);
        temp.Add(hero.economiCproportion);
        temp.Add(hero.contrapositionEconomicDifference);
        temp.Add(hero.proportionOfInjuries);
        return temp != null;
    }

    public void RefrshVertDist(HeroData hero)
    {
        if(ConvertData(hero, out var temp))
        {
            vertDist.Clear();
            vertDist = temp;
            sides = vertDist.Count;
            //设置Layout布局、Vertices顶点和Material材质为Dirty;当一个Canvas被标记为包含需要被rebatch的几何图形,那这个Canvas被认为dirty,简单来说,就是图形会重新绘制.
            SetVerticesDirty();
        }
    }

    //绘制函数,一开始就会调用这个函数去绘制图形
    protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();
        if (vertDist == null || vertDist.Count == 0) return;
        //degrees: 度, size边数, vertSize: 顶点数, adaptPos适配位置, dist 距离
        float degrees = 360f / sides;
        int vertSize = sides + 1;
        float adaptPos = -rectTransform.pivot.x * size;
        Vector2 pre = Vector2.zero;
        Vector2 poszero = Vector2.zero;
        if (vertDist.Count < vertSize) vertDist.Add(vertDist[0]);
        vertDist[vertDist.Count - 1] = vertDist[0];
        for (int i = 0; i < vertDist.Count; i++)
        {
            float dist = adaptPos * vertDist[i];
            //Deg2Rad:把角度值转换为弧度值
            float rad = Mathf.Deg2Rad * i * degrees + rotation;
            float cos = Mathf.Cos(rad);
            float sin = Mathf.Sin(rad);
            Vector2 pos0 = pre;
            Vector2 pos1 = new Vector2(dist * sin, dist * cos);
            pre = pos1;
            vh.AddUIVertexQuad(SetVertexs(new[] { pos0, pos1, poszero, poszero }));
        }
    }

    private UIVertex[] SetVertexs(Vector2[] vertices)
    {
        UIVertex[] uiVertices = new UIVertex[4];
        Vector2[] uvs = new Vector2[] { new Vector2( 0, 1 ), new Vector2(1, 1), new Vector2(1, 0), new Vector2(0, 0)};
        for(int i = 0; i < vertices.Length; i++)
        {
            var vert = UIVertex.simpleVert;
            vert.color = color;
            vert.position = vertices[i];
            vert.uv0 = uvs[i];
            uiVertices[i] = vert;
        }
        return uiVertices;
    }
    protected override void OnDestroy()
    {
        vertDist.Clear();
        vertDist = null;
    }
}
  • Test类:生成数据去调用UI_Radar重绘
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
   private float time = 1f;
   private UI_Radar UI_Radar;
   private int index;
   private Text heroName;
   public HeroData[] heros;
   private void Start()
   {
       UI_Radar = FindObjectOfType<UI_Radar>();
       heroName = transform.Find("Name").GetComponent<Text>();
   }
   private void Update()
   {
       if (heros == null || heros.Length == 0) return;
       if(index < heros.Length)
       {
           time -= Time.deltaTime;
           if (time < 0f)
           {
               //更新数据
               UI_Radar.transform.localScale = Vector3.zero;
               UI_Radar.RefrshVertDist(heros[index]);
               //动画事件
               UI_Radar.transform.DOScale(Vector3.one, 1f);
               heroName.text = heros[index].heroName;
               index++;
               time = 5f;
           }
       }
   }
   private void OnDestroy()
   {
       UI_Radar = null;
       heros = null;
   }
}
  • HeroData类:数据类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "heroData", menuName = "Hero/HeroData")]
public class HeroData : ScriptableObject
{
   [Header("选手名字")]
   public string heroName;
   [Header("分均伤害")]
   [Range(0, 2f)]
   public float averageDamage;
   [Header("伤害转换")]
   [Range(0, 2f)]
   public float damageConversion;
   [Header("伤害占比")]
   [Range(0, 2f)]
   public float damagepercentage;
   [Header("场均击杀")]
   [Range(0, 2f)]
   public float fieldAverageKill;
   [Header("生存能力")]
   [Range(0, 2f)]
   public float Viability;
   [Header("经济占比")]
   [Range(0, 2f)]
   public float economiCproportion;
   [Header("对位经济差")]
   [Range(0, 2f)]
   public float contrapositionEconomicDifference;
   [Header("承伤占比")]
   [Range(0, 2f)]
   public float proportionOfInjuries;

}

七、总结

本篇文章参考了其他博主的博客,大家如果看到这里,也可以去多多支持这些博主.
Unity_雷达图(属性图)+ UI动画_unity 雷达图_GREAT1217的博客-CSDN博客
Unity动态构建Mesh来绘制任意多边形(雷达图效果)_unity shader 3d锥形雷达罩_林新发的博客-CSDN博客
以前只用UGUI去实现UI系统,但从未思考过UGUI的底层应该如何实现的,后续有时间会不断更新关于UGUI的功能.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值