自我介绍
广东双非一本的大三小白,计科专业,想在制作毕设前夯实基础,毕设做出一款属于自己的游戏!
雷达图
先来看看效果图
要达到这个效果只需要三个脚本,一个是管理所有雷达点的,一个是控制单个雷达点的,还有一个是在Editor
- RadarChartHandler.cs 单个雷达点,并不需要加载到任何一个物体上,由管理器来管理加载
- RadarChart.cs 管理多个雷达点
- RadarChartEditor.cs 编辑器
RadarChartHandler.cs
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class RadarChartHandler : MonoBehaviour, IDragHandler
{
private Image _image;
private Image Image
{
get
{
if (_image == null) _image = GetComponent<Image>();
return _image;
}
}
private RectTransform _rect;
private RectTransform Rect
{
get
{
if (_rect == null) _rect = GetComponent<RectTransform>();
return _rect;
}
}
public void SetParent(Transform parent) => transform.SetParent(parent);
public void ChangeSprite(Sprite sprite) => Image.sprite = sprite;
public void ChangeColor(Color color) => Image.color = color;
public void SetPos(Vector2 pos) => Rect.anchoredPosition = pos;
public void SetSize(Vector2 size) => Rect.sizeDelta = size;
public void SetScale(Vector3 scale) => Rect.localScale = scale;
public void OnDrag(PointerEventData eventData)
{
Rect.anchoredPosition += eventData.delta / Rect.lossyScale.x;
}
}
RadarChart.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RadarChart : Image
{
[SerializeField] int _pointCount; //点的数量
[SerializeField] List<Vector2> _points = new List<Vector2>();
[SerializeField] Vector2 _pointSize = new Vector2(10, 10);
[SerializeField] Sprite _pointSprite;
[SerializeField] Color _pointColor = Color.white;
[SerializeField] List<float> _radarPointRadio;
[SerializeField] List<RadarChartHandler> _handlers = new List<RadarChartHandler>();
void Update()
{
SetVerticesDirty();
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
if (_radarPointRadio.Count <= 0) return;
//添加顶点
vh.AddVert(Vector3.zero, this.color, Vector2.zero); //增加轴心点的顶点
for (int i = 0; i < _radarPointRadio.Count; i++)
{
vh.AddVert(_handlers[i].transform.localPosition, this.color, Vector2.zero);
}
//AddVertsTemplete(vh); //测试用,增加五个点
//添加三角形
for (int i = 1; i < _radarPointRadio.Count; i++)
{
vh.AddTriangle(0, i + 1, i); //绘图举例 0,2,1,顺时针绘图,顺时针代表正面,逆时针代表反面,UIshader关闭了背面剔除
}
vh.AddTriangle(0, _radarPointRadio.Count, 1);
}
private void AddVertsTemplete(VertexHelper vh)
{
vh.AddVert(_handlers[0].transform.localPosition, this.color, new Vector2(0.5f, 1));
vh.AddVert(_handlers[1].transform.localPosition, this.color, new Vector2(0f, 1));
vh.AddVert(_handlers[2].transform.localPosition, this.color, new Vector2(0f, 0));
vh.AddVert(_handlers[3].transform.localPosition, this.color, new Vector2(1f, 0));
vh.AddVert(_handlers[4].transform.localPosition, this.color, new Vector2(1f, 1));
}
public void InitRadar()
{
Clear();
InitPoint();
InitHandlers();
}
public void Clear()
{
_points.Clear();
_handlers.Clear();
int count = this.transform.childCount;
if (count != 0)
{
for (int i = 0; i < count; i++)
DestroyImmediate(this.transform.GetChild(0).gameObject);
}
}
/// <summary>
/// 初始化Point
/// </summary>
private void InitPoint()
{
if (_radarPointRadio.Count <= 0) return;
//生成并设置point的位置
float radian = 2 * Mathf.PI / _radarPointRadio.Count; //弧度
float radius = 100;
float curRadian = 2 * Mathf.PI / 4.0f; //起点
for (int i = 0; i < _radarPointRadio.Count; i++)
{
float x = Mathf.Cos(curRadian) * radius;
float y = Mathf.Sin(curRadian) * radius;
curRadian += radian;
_points.Add(new Vector2(x, y));
}
}
/// <summary>
/// 初始化Handlers
/// </summary>
private void InitHandlers()
{
//生成handlers
RadarChartHandler handler = null;
for (int i = 0; i < _radarPointRadio.Count; i++)
{
GameObject point = new GameObject("Handler" + i);
point.AddComponent<RectTransform>();
point.AddComponent<Image>();
handler = point.AddComponent<RadarChartHandler>();
handler.SetParent(transform);
handler.ChangeSprite(_pointSprite);
handler.ChangeColor(_pointColor);
handler.SetSize(_pointSize);
handler.SetScale(Vector3.one);
_handlers.Add(handler);
}
//设置handler的位置
for (int i = 0; i < _radarPointRadio.Count; i++)
{
_handlers[i].SetPos(_points[i] * _radarPointRadio[i]);
}
}
}
RadarChartEditor.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(RadarChart), true)]
[CanEditMultipleObjects]
public class RadarChartEditor : UnityEditor.UI.ImageEditor
{
SerializedProperty _pointCount;
SerializedProperty _pointSprite;
SerializedProperty _pointColor;
SerializedProperty _pointSize;
SerializedProperty _radarPointRadio;
protected override void OnEnable()
{
base.OnEnable();
_pointCount = serializedObject.FindProperty("_pointCount");
_pointSprite = serializedObject.FindProperty("_pointSprite");
_pointColor = serializedObject.FindProperty("_pointColor");
_pointSize = serializedObject.FindProperty("_pointSize");
_radarPointRadio = serializedObject.FindProperty("_radarPointRadio");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
EditorGUILayout.PropertyField(_pointCount);
EditorGUILayout.PropertyField(_pointSprite);
EditorGUILayout.PropertyField(_pointColor);
EditorGUILayout.PropertyField(_pointSize);
EditorGUILayout.PropertyField(_radarPointRadio, true);
RadarChart radar = target as RadarChart;
if (radar != null)
{
if (GUILayout.Button("生成雷达图"))
{
radar.InitRadar();
}
if (GUILayout.Button("Clear"))
{
radar.Clear();
}
}
serializedObject.ApplyModifiedProperties();
if (GUI.changed)
{
EditorUtility.SetDirty(target);
}
}
}
使用,只需要一个空物体即可
笔记
-
RadarChartHandler.cs:
- 在 OnDrag 方法中,使用的是
Rect.anchoredPosition += eventData.delta / Rect.lossyScale.x;
为什么后面要除上一个lossyScale呢,lossyScale是一个全局(世界)的Scale值,比如父物体的scale值是 (2,2,2) 那么 eventData.delta 也会对应的乘上父物体的scale值造成拖动的时候产生偏移,因此需要除以全局scale值来消除偏移值
- 在 OnDrag 方法中,使用的是
-
RadarChart.cs:
- UV值只有 [0,1]
- 画图的时候是先画顶点再画三角形,且顺时针画三角形呈现的是正面,逆时针画三角形呈现的是背面,不过UI的shader并没有开启背面剔除,所以顺逆着画对视觉上并没有任何影响,不过还是需要知道的
- 添加顶点的时候
vh.AddVert(Vector3.zero, this.color, Vector2.zero); //增加轴心点的顶点
增加了轴心点的顶点是因为要如下画图
-
左边(有轴心点)画图,012,023,034,045,051
-
右边(无轴心点)画图,012,123,234,340,虽然右边的图少画了一点,但是倘若拖动雷达点从A点移动至B点,因为线段02的存在,会导致出现橘色的部分
- 比如按顶点 0 -> 1 -> 2画(顺时针),便是画正面
- 倘若按顶点 3 -> 2 -> 1画(逆时针),便是画背面