【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画(逆时针),便是画背面

在这里插入图片描述

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