【二】開發一個類似於unity profile的數據可視化面板

接上篇博客

前一篇有一個侷限性:數據是死的,也就是說必須先獲取到數據,再生成圖表。可unity proflier基本上是實時獲取數據並呈現的,那麼之前的方案就行不通了。

先上效果圖
在這裏插入圖片描述
這裏是實時獲取monoUsed數

原理就是先生成N個爲0的數據,然後每點擊一次button添加一個新的數據,刪除一個末尾的數據,這樣就可以動態的顯示了。
代碼和上一篇大同小異,無非多了一個增加數據和刪除數據的方法。

代碼:

using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;

public class DataClass
{
    public int[] data = new int[1];
}

public class Paint : EditorWindow
{
    List<DataClass> list = new List<DataClass>();
    DataClass data = null;
    private Material _graphMaterial;

    [MenuItem("#############Paint##############/Paint")]
    public static void ShowWindow()
    {
        Rect wr = new Rect(0, 0, 1000, 500);
        Paint window = (Paint)GetWindowWithRect(typeof(Paint), wr, true, "Paint");
        window.wantsMouseMove = false;
        window.Show();
        window.Focus();
    }

    const int LAYERS = 1;

    private GUIStyle _headStyle;
    private Rect _axisRect = new Rect(170, 250 - 250, 800, 300);
    private Rect _graphRect = new Rect(170, 270 - 250, 760, 280);
    public int _index = 10;
    private Rect _graphContentRect = new Rect(170, 270 - 250, 760, 280);

    private Color[] _layerColor = new Color[LAYERS]
    {
            new Color(220f / 255f, 20f / 255f, 60f / 255f),
            //new Color(255f / 255f, 165f / 255f, 0f / 255f),
            //new Color(255f / 255f, 255f / 255f, 0f / 255f),
            //new Color(124f / 255f, 252f / 255f, 0f / 255f),
            //new Color(0f / 255f, 255f / 255f, 255f / 255f),
            //new Color(0f / 255f, 0f / 255f, 255f / 255f),
            //new Color(128f / 255f, 0f / 255f, 128f / 255f),
    };

    private Vector3[][] _points = new Vector3[LAYERS][];
    //當前鼠標指向的x軸座標
    private int _current;
    private bool _clickGraph;

    //採樣數據
    private int _sampleCount;
    private List<DataClass> _samples = new List<DataClass>();


    private void OnEnable()
    {
        //捕獲數據
        _samples = GenFackeData();
        //數據橫座標大小
        _sampleCount = _samples.Count;
    }

    private void OnGUI()
    {
        if (_headStyle == null)
        {
            _headStyle = new GUIStyle();
            _headStyle.fontSize = 15;
            _headStyle.alignment = TextAnchor.MiddleCenter;
            _headStyle.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
        }
        if (_graphMaterial == null)
        {
            _graphMaterial = new Material(Shader.Find("Hidden/Internal-Colored"));
            _graphMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
            _graphMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
            _graphMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
            _graphMaterial.SetInt("_ZWrite", 0);
        }

        if (EditorApplication.isPlaying)
        {
            DrawGraph();
            //鼠標交互
            HandleEvent();
        }
        if (GUI.Button(new Rect(50, 320, 120, 20), "獲取新數據"))
        {
            AddData();
            DelectData();
            _samples = list;
            _sampleCount = _samples.Count + 1;
        }
    }
    //劃線算法核心
    public void DrawGraph()
    {
        if (_points[0] == null || _points[0].Length != _sampleCount)
        {
            for (int layer = 0; layer < LAYERS; ++layer)
                _points[layer] = new Vector3[_sampleCount];
        }

        float maxValue = GetListMaxValue(_samples);
        float avgValue = GetListAverageValue(_samples);

        //劃線算法核心
        for (int i = 0; i < _samples.Count; ++i)
        {
            for (int layer = 0; layer < LAYERS; layer++)
            {
                _points[layer][i].x = (float)i / _sampleCount * _graphContentRect.width + _graphContentRect.xMin;
                _points[layer][i].y = _graphContentRect.yMax - _samples[i].data[layer] / maxValue * _graphContentRect.height;
            }
        }

        //填充顏色 
        //畫點連線
        //_graphMaterial.SetPass(0);
        //for (int layer = 0; layer < LAYERS; ++layer)
        //{

        //    GL.Begin(GL.TRIANGLE_STRIP);
        //    GL.Color(_layerColor[layer]);
        //    for (int i = 0; i < _samples.Count; ++i)
        //    {
        //        if (_graphRect.Contains(_points[layer][i]))
        //        {
        //            //提交頂點
        //            GL.Vertex(_points[layer][i]);
        //            if (layer == LAYERS - 1)
        //                GL.Vertex3(_points[layer][i].x, _graphContentRect.yMax, 0);
        //            else
        //                GL.Vertex(_points[layer + 1][i]);
        //        }
        //    }
        //    GL.End();
        //}


        //連點成線
        Handles.BeginGUI();
        for (int layer = 0; layer < LAYERS; ++layer)
        {
            Handles.color = _layerColor[layer];
            Handles.DrawAAPolyLine(_points[layer].Where(p => _graphRect.Contains(p)).ToArray());
        }
        Handles.EndGUI();

        //鼠標定位線
        if (_graphRect.Contains(_points[0][_current]))
        {
            Handles.BeginGUI();
            Handles.color = Color.white;
            //縱向線
            Handles.DrawAAPolyLine(5, new Vector2(_points[0][_current].x, _points[0][_current].y), new Vector2(_points[0][_current].x, _axisRect.yMax));
            Handles.EndGUI();
            //橫向座標數據
            //EditorGUI.LabelField(new Rect(_points[0][_current].x, _axisRect.yMin + 310, 50, 50), _current.ToString() + "/" + (_sampleCount - 1));
            //縱向座標數據
            for (int i = 0; i < LAYERS; i++)
            {
                EditorGUI.LabelField(new Rect(_points[i][_current].x, _points[i][_current].y, 20, 20), _samples[_current].data[i].ToString());
            }
        }
        string detail = string.Format(
            "<color={0}>monoUsed:{1:N0}</color>",
            Color2String(_layerColor[0]), _samples[_current].data[0]);
        EditorGUI.LabelField(new Rect(_axisRect.center.x - 400, _axisRect.yMax + 20, 800, 50), detail, _headStyle);

        //座標軸
        DrawArrow(new Vector2(_axisRect.xMin, _axisRect.yMax), new Vector2(_axisRect.xMin, _axisRect.yMin), Color.white);
        DrawArrow(new Vector2(_axisRect.xMin, _axisRect.yMax), new Vector2(_axisRect.xMax, _axisRect.yMax), Color.white);
    }
    //鼠標交互
    public void HandleEvent()
    {
        var point = Event.current.mousePosition;
        switch (Event.current.type)
        {
            //滑動鼠標
            case EventType.MouseDrag:

                if (Event.current.button == 0 && _clickGraph)
                {
                    UpdateCurrentSample();
                    Repaint();
                }

                if (Event.current.button == 2 && _clickGraph)
                {
                    _graphContentRect.x += Event.current.delta.x;
                    if (_graphContentRect.x > _graphRect.x)
                        _graphContentRect.x = _graphRect.x;
                    if (_graphContentRect.xMax < _graphRect.xMax)
                        _graphContentRect.x = _graphRect.xMax - _graphContentRect.width;
                    Repaint();
                }
                break;

            //單擊鼠標左鍵
            case EventType.MouseDown:
                //如果集合內獲取過點 就肯定按下了鼠標
                _clickGraph = _graphRect.Contains(point);

                if (_clickGraph)
                    //EditorGUI.FocusTextInControl(null);

                    if (Event.current.button == 0 && _clickGraph)
                    {
                        UpdateCurrentSample();
                        Repaint();
                    }
                if (Event.current.button == 1)
                {
                    //DrawFloatMenu();
                    Repaint();
                }
                break;

            //鍵盤左右鍵
            case EventType.KeyDown:

                if (Event.current.keyCode == KeyCode.LeftArrow)
                    SetCurrentIndex(_current - 1);
                if (Event.current.keyCode == KeyCode.RightArrow)
                    SetCurrentIndex(_current + 1);
                Repaint();
                break;
        }
    }

    private void UpdateCurrentSample()
    {
        float x = Event.current.mousePosition.x;
        float distance = float.MaxValue;
        int index = 0;
        for (int i = 0; i < _points[0].Length; ++i)
        {
            //找最近距離的點
            if (_graphRect.Contains(_points[0][i]) && Mathf.Abs(x - _points[0][i].x) < distance)
            {
                distance = Mathf.Abs(x - _points[0][i].x);
                index = i;
            }
        }
        //設置當前點
        SetCurrentIndex(index);
    }

    private void SetCurrentIndex(int i)
    {
        _current = Mathf.Clamp(i, 0, _samples.Count - 1);
    }

    //Color轉string
    private string Color2String(Color color)
    {
        string c = "#";
        //ToString("X2")轉換爲大寫16進制
        c += ((int)(color.r * 255)).ToString("X2");
        c += ((int)(color.g * 255)).ToString("X2");
        c += ((int)(color.b * 255)).ToString("X2");
        return c;
    }

    //畫座標軸
    private void DrawArrow(Vector2 from, Vector2 to, Color color)
    {
        Handles.BeginGUI();
        Handles.color = color;
        Handles.DrawAAPolyLine(3, from, to);
        Vector2 v0 = from - to;
        v0 *= 10 / v0.magnitude;
        Vector2 v1 = new Vector2(v0.x * 0.866f - v0.y * 0.5f, v0.x * 0.5f + v0.y * 0.866f);
        Vector2 v2 = new Vector2(v0.x * 0.866f + v0.y * 0.5f, v0.x * -0.5f + v0.y * 0.866f); ;
        Handles.DrawAAPolyLine(3, to + v1, to, to + v2);
        Handles.EndGUI();
    }

    private int GetListMaxValue(List<DataClass> list)
    {
        List<int> newList = new List<int>();
        for (int i = 0; i < list.Count; i++)
        {
            for (int j = 0; j < LAYERS; j++)
            {
                newList.Add(list[i].data[j]);
            }
        }
        return newList.Max();
    }

    private float GetListAverageValue(List<DataClass> list)
    {
        List<int> newList = new List<int>();
        for (int i = 0; i < list.Count; i++)
        {
            for (int j = 0; j < LAYERS; j++)
            {
                newList.Add(list[i].data[j]);
            }
        }
        return (float)newList.Average();
    }

    //捕獲數據
    private List<DataClass> GenFackeData()
    {
        for (int i = 0; i < 100; i++)
        {
            data = new DataClass();
            data.data[0] = 0;
            list.Add(data);
        }
        return list;
    }
    //新加數據
    private void AddData()
    {
        data = new DataClass();
        var monoUsed = (Profiler.GetMonoUsedSizeLong() >> 10) / 1024f;
        data.data[0] = (int)monoUsed;  
        list.Add(data);
    }
    //摒棄末尾數據
    private void DelectData()
    {
        list.RemoveAt(0);
    }
}

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