一年前寫過一篇折線圖的製作,當時顯示效果還可以,只不過因爲之前剛接觸寫博客,所以寫的內容不是很完善,加上比較忙,都是草率記錄的,代碼結構也不是很好。昨天我又把這個項目的代碼熟悉了一遍,然後把代碼更改精煉了下。應該比以前更容易讀懂了。
代碼如下,上面都有註釋:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
//折線圖
public class LineChart : MaskableGraphic,ICanvasRaycastFilter
{
[SerializeField]//曲線個數
private int lineCount = 1;
[SerializeField]//顏色
private List<Color> lineColors = new List<Color>() { };
[SerializeField]
private int xwidth = 20;//數據間隔
private List<float> pointList = new List<float>();
private Vector3 pos;//數據點的座標
private RectTransform rectTransform;
private Text numText;
//兩個數據之間的間隔
private float xLength;
//最多顯示的數據數量
private const int RemainCount = 50;
public Vector3 Pos
{
get
{
return pos;
}
}
protected override void Awake()
{
base.Awake();
rectTransform = GetComponent<RectTransform>();
xLength = transform.parent.parent.GetComponent<RectTransform>().sizeDelta.x;
numText = transform.Find("NumText").GetComponent<Text>();
}
private bool IsReachLength = false;
public void AddPoint(float point)
{
pointList.Add(point);
int count = pointList.Count;
if (count> lineCount)//如果只有一條曲線,則至少有兩個點纔可以開始繪製曲線
{
Vector2 size = rectTransform.sizeDelta;
//此函數改變RectTransform的大小 可以觸發OnPopulateMesh調用
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, xwidth * (count)); //當數據量達到我們設定的顯示上限 數據個數保持不變 這個時候設置他的大小是不發生變化的
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, xwidth * (count+1));//所以我們就先設置小一單位 在設置加一單位 保證大小變化
if (size.x> xLength)//顯示區域的大小
{
if (count > RemainCount)//當數據個數大於我們規定的顯示個數 就需要移除前面的數據
{
pointList.RemoveAt(0);
Vector3 pos = transform.localPosition;
transform.localPosition = pos + new Vector3(xwidth, 0, 0);//把顯示往前移動一個單位 然後做移動動畫
}
transform.DOLocalMoveX(transform.localPosition.x-xwidth,0.3f);
}
}
}
protected override void OnPopulateMesh(VertexHelper vh)
{
int _count = pointList.Count;
//畫線
if (_count > lineCount)
{
vh.Clear();
for (int i = 0; i < _count - lineCount; i++)
{
//讓曲線寬度在各種斜率下寬度一致
float k = (pointList[i + lineCount] - pointList[i]) / (xwidth);
float _y = Mathf.Sqrt(Mathf.Pow(k, 2) + 4);
_y = Mathf.Abs(_y);
UIVertex[] verts = new UIVertex[4];
verts[0].position = new Vector3(xwidth * (i / lineCount), pointList[i] - _y / 2);
verts[1].position = new Vector3(xwidth * (i / lineCount), _y / 2 + pointList[i]);
verts[2].position = new Vector3(xwidth * ((i + lineCount) / lineCount), pointList[i + lineCount] + _y / 2);
verts[3].position = new Vector3(xwidth * ((i + lineCount) / lineCount), pointList[i + lineCount] - _y / 2);
for(int j=0;j<4;j++)
{
verts[j].color = lineColors[(i % lineCount)];
verts[j].uv0 = Vector2.zero;
}
vh.AddUIVertexQuad(verts);
}
}
//draw quad 顯示數據大小的方塊點
for (int i = 0; i < _count; i++)
{
UIVertex[] quadverts = new UIVertex[4];
quadverts[0].position = new Vector3((i / lineCount) * xwidth - 1.5f, pointList[i] - 1.5f);
quadverts[0].color = Color.white;
quadverts[0].uv0 = Vector2.zero;
quadverts[1].position = new Vector3((i / lineCount) * xwidth - 1.5f, pointList[i] + 1.5f);
quadverts[1].color = Color.white;
quadverts[1].uv0 = Vector2.zero;
quadverts[2].position = new Vector3((i / lineCount) * xwidth + 1.5f, pointList[i] + 1.5f);
quadverts[2].color = Color.white;
quadverts[2].uv0 = Vector2.zero;
quadverts[3].position = new Vector3((i / lineCount) * xwidth + 1.5f, pointList[i] - 1.5f);
quadverts[3].color = Color.white;
quadverts[3].uv0 = Vector2.zero;
vh.AddUIVertexQuad(quadverts);
}
}
//如果鼠標在數據點上 就會返回true
public bool IsRaycastLocationValid(UnityEngine.Vector2 sp, UnityEngine.Camera eventCamera)
{
Vector2 local;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, sp, eventCamera, out local);
Rect rect = GetPixelAdjustedRect();
local.x += rectTransform.pivot.x * rect.width;
local.y += rectTransform.pivot.y * rect.height;
int _count = pointList.Count;
for (int i=0;i< _count; i++)
{
if (local.x > (i / lineCount) * xwidth - 3f && local.x < ((i / lineCount) * xwidth + 3f) && local.y > (pointList[i] - 3f)
&& local.y < (pointList[i] + 3f))
{
pos = new Vector3((i / lineCount) * xwidth, pointList[i], 0);
return true;
}
}
return false;
}
void Update()
{
//鼠標是否放在白點處
if(IsRaycastLocationValid(Input.mousePosition,null))
{
numText.gameObject.SetActive(true);
numText.text = (pos.y).ToString();
numText.transform.localPosition = pos;
}
else
{
numText.gameObject.SetActive(false);
}
}
}
然後我們在面板上創建一個Scroll View更改名字爲LineChart(命名隨意),因爲我們不需要顯示滑動條,所以將Scroll View下的兩個滑動條都給刪除掉。然後將我們上面的腳本加到子目錄Content上。如下圖:
因爲我們設置的以左下角爲原點,向右爲X軸,向上爲Y軸,所以我們設置Content的Anchors和Pivot都爲0,如下圖:
設置已經完成,這時候我們只需在外部調用往裏添加數據就可以了。
public class Test : MonoBehaviour {
public LineChart lineChart;
// Use this for initialization
IEnumerator Start ()
{
while(true)
{
lineChart.AddPoint(Random.Range(10.0f,50.0f));
yield return new WaitForSeconds(2f);
}
}
}
還有就是我們要實現鼠標移動到數據上顯示數據的值,這時候我們需要在Content下創建一個Text,命名爲NumText,設置他的Pivot爲(0.5,-0.2)。這樣可以讓文本顯示在數上0.2單位處。偏移量你可以自己設定。
以上就是對摺線圖製作的更新。對於繪製曲線以及原理,可以觀看第一篇折線圖博客。點擊打開鏈接。希望本博客對你有幫助!