自我介紹
廣東雙非一本的大三小白,計科專業,想在製作畢設前夯實基礎,畢設做出一款屬於自己的遊戲!
雷達圖
先來看看效果圖
要達到這個效果只需要三個腳本,一個是管理所有雷達點的,一個是控制單個雷達點的,還有一個是在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畫(逆時針),便是畫背面