一个画图表(折线图,树状图,饼状图,雷达图)的插件。
底层使用UGUI中的重写了OnPopulateMesh这个方法, 用来实现鼠标画线的功能。
OnPopulateMesh(VertexHelper vh)
{
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public enum LineType
{
Straight,
Smooth
}
public class DrawLineComponent : Graphic
{
[Header("线的属性")]
[SerializeField] private LineType lineType = LineType.Straight;
[SerializeField] private Line line = new StraightLine();
[SerializeField] protected float m_ChartWidth;
[NonSerialized] private bool m_RefreshChart = false;
public float chartWidth { get { return m_ChartWidth; } }
public LineType LineType
{
get => lineType;
set
{
lineType = value;
if (value == LineType.Straight)
line = new StraightLine();
else
line = new SmoothLine();
m_RefreshChart = true;
}
}
protected override void Awake()
{
base.Awake();
m_ChartWidth = rectTransform.sizeDelta.x;
if (LineType == LineType.Smooth)
line = new SmoothLine();
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
line.DrawLine(vh);
}
private void Update()
{
CheckRefreshChart();
}
protected void CheckRefreshChart()
{
if (m_RefreshChart)
{
int tempWid = (int)chartWidth;
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, tempWid - 1);
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, tempWid);
m_RefreshChart = false;
}
}
public void AddPoint(Vector3 v3)
{
line.AddPoint(v3);
m_RefreshChart = true;
}
public void AddPoint(List<Vector3> points)
{
line.AddPoint(points);
m_RefreshChart = true;
}
public void SetSize(float size)
{
line.size = size;
m_RefreshChart = true;
}
public void SetColor(Color color)
{
line.lineColor = color;
m_RefreshChart = true;
}
public void SetSmoothness(float smoothness)
{
if (LineType == LineType.Smooth)
((SmoothLine)line).smoothness = smoothness;
m_RefreshChart = true;
}
public void SetLineSmoothStyle(float smoothStyle)
{
if (LineType == LineType.Smooth)
((SmoothLine)line).lineSmoothStyle = smoothStyle;
m_RefreshChart = true;
}
}
[System.Serializable]
public class Line
{
[SerializeField]
protected List<Vector3> dataPoints = new List<Vector3>();
[SerializeField] public float size = 1;
[SerializeField] public Color lineColor = Color.black;
public virtual void DrawLine(VertexHelper vh)
{
}
public void AddPoint(Vector3 p)
{
dataPoints.Add(p);
}
public void AddPoint(List<Vector3> points)
{
dataPoints.AddRange(points);
}
}
public class StraightLine : Line
{
public override void DrawLine(VertexHelper vh)
{
for (int i = 0; i < dataPoints.Count; i++)
{
if (i < dataPoints.Count - 1)
{
UIDrawLine.DrawLine(vh, dataPoints[i], dataPoints[i + 1], size, lineColor);
}
}
}
}
public class SmoothLine:Line
{
/// <summary>
//曲线平滑度。值越小曲线越平滑,但顶点数也会随之增加。
/// </summary>
[SerializeField] public float smoothness = 2;
/// <summary>
/// 曲线平滑系数。通过调整平滑系数可以改变曲线的曲率,得到外观稍微有变化的不同曲线。
/// </summary>
[SerializeField] public float lineSmoothStyle = 2;
private List<Vector3> bezierPoints = new List<Vector3>();
public override void DrawLine(VertexHelper vh)
{
Vector3 lp = Vector3.zero;
Vector3 np = Vector3.zero;
Vector3 llp = Vector3.zero;
Vector3 nnp = Vector3.zero;
for (int i = 0; i < dataPoints.Count; i++)
{
if (i < dataPoints.Count - 1)
{
llp = i > 1 ? dataPoints[i - 2] : lp;
nnp = i < dataPoints.Count - 1 ? dataPoints[i + 1] : np;
UIDrawLine.GetBezierList(ref bezierPoints, dataPoints[i], dataPoints[i + 1], llp, nnp, smoothness, lineSmoothStyle);
for (int j = 0; j < bezierPoints.Count; j++)
{
if (j < bezierPoints.Count - 1)
{
UIDrawLine.DrawLine(vh, bezierPoints[j], bezierPoints[j + 1], size, lineColor);
}
}
}
}
}
}
画线的脚本
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 画线
/// https://github.com/monitor1394/unity-ugui-XCharts
/// </summary>
public class UIDrawLine
{
private static UIVertex[] vertex = new UIVertex[4];
public static void DrawLine(VertexHelper vh, Vector3 p1, Vector3 p2, float size, Color32 color)
{
if (p1 == p2) return;
Vector3 v = Vector3.Cross(p2 - p1, Vector3.forward).normalized * size;
vertex[0].position = p1 - v;
vertex[1].position = p2 - v;
vertex[2].position = p2 + v;
vertex[3].position = p1 + v;
for (int j = 0; j < 4; j++)
{
vertex[j].color = color;
vertex[j].uv0 = Vector2.zero;
}
vh.AddUIVertexQuad(vertex);
}
public static void GetBezierList(ref List<Vector3> posList, Vector3 sp, Vector3 ep,
Vector3 lsp, Vector3 nep, float smoothness = 2f, float k = 2.0f)
{
float dist = Mathf.Abs(sp.x - ep.x);
Vector3 cp1, cp2;
var dir = (ep - sp).normalized;
var diff = dist / k;
if (lsp == sp)
{
cp1 = sp + dist / k * dir * 1;
cp1.y = sp.y;
cp1 = sp;
}
else
{
cp1 = sp + (ep - lsp).normalized * diff;
}
if (nep == ep) cp2 = ep;
else cp2 = ep - (nep - sp).normalized * diff;
dist = Vector3.Distance(sp, ep);
int segment = (int)(dist / (smoothness <= 0 ? 2f : smoothness));
if (segment < 1) segment = (int)(dist / 0.5f);
if (segment < 4) segment = 4;
GetBezierList2(ref posList, sp, ep, segment, cp1, cp2);
}
public static void GetBezierList2(ref List<Vector3> posList, Vector3 sp, Vector3 ep, int segment, Vector3 cp,
Vector3 cp2)
{
posList.Clear();
if (posList.Capacity < segment + 1)
{
posList.Capacity = segment + 1;
}
for (int i = 0; i < segment; i++)
{
posList.Add((GetBezier2(i / (float)segment, sp, cp, cp2, ep)));
}
posList.Add(ep);
}
public static Vector3 GetBezier2(float t, Vector3 sp, Vector3 p1, Vector3 p2, Vector3 ep)
{
t = Mathf.Clamp01(t);
var oneMinusT = 1f - t;
return oneMinusT * oneMinusT * oneMinusT * sp +
3f * oneMinusT * oneMinusT * t * p1 +
3f * oneMinusT * t * t * p2 +
t * t * t * ep;
}
}
public class TestMouse : MonoBehaviour
{
[SerializeField] private DrawLineComponent dl;
void Start()
{
dl.SetSize(2);
}
void Update()
{
if(Input.GetMouseButton(0))
{
Debug.Log(Input.mousePosition);
dl.AddPoint(Input.mousePosition);
}
}
}