赞
踩
之前面试的时候,有面试官问了我关于怎么根据数据去制作UI雷达图的,当时没有回答出来,问了室友说可以用Mesh网格去实现,这两天又在公司看到了有关于这类相关的代码,所以自己想实现一下,文章仅限参考.
MaskableGraphic
:UGUI的核心组件,它继承自Graphic.MashableGraphic是一个抽象类,它的派生类有RawImage
,Image
, Text
,顾名思义,MaskableGraphic是可遮罩的图像.
VertexHelper
:VertexHelper是UI绘制的一个帮助类,绘制之前需要先调用vh.Clear(),将原有的网格UI清空.
- 创建类:写一个类于Image组件的类,继承于MaskableGraphic类
- 重绘图形:在类中继承MaskableGraphicd.OnPoPulateMesh(VertexHelper vh)函数.
- 添加顶点和图形:使用VertexHelper中的AddVert(添加顶点)或AddUIVerttexQuad(创建四边形)函数等,绘制图形
- 度数(degree): 根据传入的数据的数量去平分一个圆的360°,得到每个数据的度数 360 / Count(数据数量)
- 顶点数(vertSize):由于要将图形首尾连接起来,最后一个顶点要与第一个顶点相同,所以顶点数要比数据量大1 即Count + 1.
- 长度(pos1):即顶点离中心点的距离.
dist * sin
dist * cos
根据官网去了解相关的API,本文章会讲解一下需要用的API.
- Graphic:UI.Graphic - Unity 脚本 API
- VertexHelper:UI.VertexHelper - Unity 脚本 API
- UIVertex:UnityEngine.UIVertex - Unity 脚本 API
所有视觉UI组件的基类,创建视觉UI组件时,应该继承此类.
公共函数 | 描述 |
---|---|
OnPopulateMesh | 一个回调函数,当UI元素需要生成顶点数据时,会调用这个函数 |
SetAllDirty | 将布局设为"脏" |
SetVerticesDirty | 将顶点标记为"脏" |
SetNativeSize | 调整图形大小使其像素精确 |
可辅助UI生成网格的Utility类,此类实现IDisposable,用以辅助内存管理,个人理解,这是一个类似于写入流的类,不过这个写入的是网格数据.
变量 | 描述 | 注释 |
---|---|---|
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) |
Canvas用于管理顶点的类
变量 | 描述 |
---|---|
simpleVert | 静态变量,简单UIVertex |
color | 顶点颜色 |
normal | 法线 |
position | 顶点位置 |
tangent | 切线 |
uv0 | 网格下第一个纹理坐标集 |
该项目根据LPL数据去制作的,
LPL
冲呀,The Shy
冲呀
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;
}
}
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;
}
}
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的功能.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。