在c#中使用一个类继承Image用来代替Image组件用。在该类中使用OnPopulateMesh()函数重新绘制网格,从而形成需要的图形。
其中需要先使用vertexHelper.AddVert(顶点位置,颜色,uv坐标)来添加顶点。颜色使用面板中设置的那个颜色(这里填个color即可)。由于不需要纹理贴图,uv坐标随便填个Vector2.zero即可。添加的顺序就会是这个顶点的编号,从0开始。
如何得到顶点位置:第一个顶点位置用中心位置,其它的通过计算得到。通过三角函数得到最内点和最外点的坐标(当值为0时雷达图相应那个点就会在最内点这个位置。这里最内点没有使用中心点),坐标为(距中心距离*cos(θ),距中心距离*sin(θ),0),θ为2*π/边数*顶点顺序编号。顶点编号范围0到边数-1。得到最内点和最外点坐标之后通过差值求得这两点间的那个顶点位置。当该项能力为0,则在最内点,能力增大时向外移动,能力为1时到达最外点。依次如此计算得到圆心外的各个顶点。
之后再通过vertexHelper.AddTriangle(顶点1编号,顶点2编号,顶点3编号),以3个顶点组成一个三角面片。一般需要顺时针选择顶点,虽然对UI来说顺序反了一般也能显示。这里的第顶点1都用圆心顶点,其它2个依次更换,产生需要的全部三角面片。
如果需要在运行中修改顶点位置,可在修改后用SetVerticesDirty()函数刷新顶点。
下图为一组顶点和三角面片的示例。三角面片组成了要显示的雷达图。
脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RadarImage : Image
{
[SerializeField]//让私有变量在面板显示
private int sideCount=5;//雷达图边数。这项应当在启动前设置而不要在运行中修改。
public float minDistance=5;//顶点与中心的最小距离
public float maxDistance = 50;//顶点与中心的最大距离
public float[] eachPercent;//每一项数值的大小(0~1表示的百分比)
private Vector3[] innerPositions;//雷达图最内圈的点
private Vector3[] exteriorPositions;//雷达图最外圈的点
public float initialRadian = 0;//第一个顶点的起始弧度。默认从正右方开始。
protected override void Awake()
{
eachPercent = new float[sideCount];
}
private void Update()
{
//SetVerticesDirty();//刷新顶点,可用于实时修改
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();//清除原信息
initPositions();
AddVerts(vh);
AddTriangles(vh);
}
///
/// 初始化雷达图最内圈和最外圈的点
///
private void initPositions()
{
innerPositions = new Vector3[sideCount];
exteriorPositions = new Vector3[sideCount];
float tempRadian = initialRadian;
float radiamDelta = 2 * Mathf.PI / sideCount;//每两个相邻顶点相差的弧度
for (int i = 0; i < sideCount; i++)
{
innerPositions[i] = new Vector3(minDistance * Mathf.Cos(tempRadian), minDistance * Mathf.Sin(tempRadian), 0);
exteriorPositions[i] = new Vector3(maxDistance * Mathf.Cos(tempRadian), maxDistance * Mathf.Sin(tempRadian), 0);
tempRadian += radiamDelta;
}
}
///
/// 添加形成三角面片用的顶点
///
///
private void AddVerts(VertexHelper vh)
{
vh.AddVert(Vector3.zero, color, Vector2.zero);//添加轴心点位置为第一个顶点
for (int i = 0; i < sideCount; i++)
{
//通过在最内点和最外点间差值得到雷达图顶点实际位置,并添加到为vh的顶点。由于并没有图案,最后一项的uv坐标就随便填了。
vh.AddVert(Vector3.Lerp(innerPositions[i], exteriorPositions[i], eachPercent[i]), color, Vector2.zero);
}
}
///
/// 添加三角面片
///
///
private void AddTriangles(VertexHelper vh)
{
for (int i = 0; i < sideCount-1; i++)
{
vh.AddTriangle(0, i + 1, i + 2);
}
vh.AddTriangle(0, sideCount, 1);
}
}
这里定义的变量并不能直接在面板显示,如果需要在面板显示,还需要加一个脚本并放在一个名为Editor的文件夹下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(RadarImage), true)]
[CanEditMultipleObjects]
public class RadarImageEditor : UnityEditor.UI.ImageEditor
{
SerializedProperty _sideCount;
SerializedProperty _minDistance;
SerializedProperty _maxDistance;
SerializedProperty _eachPercent;
SerializedProperty _initialRadian;
protected override void OnEnable()
{
base.OnEnable();
_sideCount = serializedObject.FindProperty("sideCount");
_minDistance = serializedObject.FindProperty("minDistance");
_maxDistance = serializedObject.FindProperty("maxDistance");
_eachPercent = serializedObject.FindProperty("eachPercent");
_initialRadian = serializedObject.FindProperty("initialRadian");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
EditorGUILayout.PropertyField(_sideCount);
EditorGUILayout.PropertyField(_minDistance);
EditorGUILayout.PropertyField(_maxDistance);
EditorGUILayout.PropertyField(_eachPercent,true);
EditorGUILayout.PropertyField(_initialRadian);
RadarImage radar = target as RadarImage;
serializedObject.ApplyModifiedProperties();
if (GUI.changed)
{
EditorUtility.SetDirty(target);
}
}
}
在启动前设置好边数(Side Count),启动后可以设置每一项的能力(Each Percent)。通过Color控制颜色和透明度。
默认从正右方开始绘制。可以修改初始弧度(Intial Radian)改变开始绘制的位置,让图像进行旋转:
覆盖在一张图片上,形成通常见到的雷达图: