【U3D/UGUI】4.雷達圖

自我介紹

廣東雙非一本的大三小白,計科專業,想在製作畢設前夯實基礎,畢設做出一款屬於自己的遊戲!

雷達圖

先來看看效果圖

在這裏插入圖片描述

要達到這個效果只需要三個腳本,一個是管理所有雷達點的,一個是控制單個雷達點的,還有一個是在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值來消除偏移值
  • 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畫(逆時針),便是畫背面

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章