297 lines
7.5 KiB
C#
297 lines
7.5 KiB
C#
using System.Collections.Generic;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
/// <summary>
|
||
/// UI兼容的图表组件
|
||
/// 使用UI Image和RectTransform在Canvas中绘制折线图,替代LineRenderer
|
||
/// </summary>
|
||
public class UIChartComponent : MonoBehaviour
|
||
{
|
||
[Header("图表设置")]
|
||
public RectTransform chartArea;
|
||
public TextMeshProUGUI valueLabel;
|
||
public TextMeshProUGUI titleLabel;
|
||
public Color lineColor = Color.green;
|
||
public float lineWidth = 2f;
|
||
|
||
[Header("数据设置")]
|
||
public int maxDataPoints = 50;
|
||
public float minValue = 0f;
|
||
public float maxValue = 100f;
|
||
public string unit = "";
|
||
|
||
[Header("UI设置")]
|
||
public GameObject linePrefab; // 预制件:包含Image组件的GameObject
|
||
public Transform lineParent; // 折线段的父物体
|
||
|
||
private List<float> dataPoints = new List<float>();
|
||
private List<GameObject> lineSegments = new List<GameObject>();
|
||
|
||
void Awake()
|
||
{
|
||
InitializeChart();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化图表
|
||
/// </summary>
|
||
private void InitializeChart()
|
||
{
|
||
// 如果没有设置lineParent,使用chartArea
|
||
if (lineParent == null && chartArea != null)
|
||
{
|
||
lineParent = chartArea;
|
||
}
|
||
|
||
// 如果没有linePrefab,创建一个默认的
|
||
if (linePrefab == null)
|
||
{
|
||
CreateDefaultLinePrefab();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建默认的线段预制件
|
||
/// </summary>
|
||
private void CreateDefaultLinePrefab()
|
||
{
|
||
linePrefab = new GameObject("LineSegment");
|
||
|
||
// 添加RectTransform
|
||
var rectTransform = linePrefab.AddComponent<RectTransform>();
|
||
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||
|
||
// 添加Image组件
|
||
var image = linePrefab.AddComponent<Image>();
|
||
image.color = lineColor;
|
||
|
||
// 设置为预制件状态
|
||
linePrefab.SetActive(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加数据点
|
||
/// </summary>
|
||
public void AddDataPoint(float value)
|
||
{
|
||
dataPoints.Add(value);
|
||
|
||
// 保持最大数据点数量
|
||
if (dataPoints.Count > maxDataPoints)
|
||
{
|
||
dataPoints.RemoveAt(0);
|
||
}
|
||
|
||
UpdateChart();
|
||
UpdateValueLabel(value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新图表显示
|
||
/// </summary>
|
||
private void UpdateChart()
|
||
{
|
||
if (chartArea == null || lineParent == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
int requiredSegmentCount = Mathf.Max(0, dataPoints.Count - 1);
|
||
EnsureLineSegmentCount(requiredSegmentCount);
|
||
|
||
var rect = chartArea.rect;
|
||
var width = rect.width;
|
||
var height = rect.height;
|
||
|
||
for (int i = 0; i < lineSegments.Count; i++)
|
||
{
|
||
var segment = lineSegments[i];
|
||
if (segment == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
bool active = i < requiredSegmentCount;
|
||
if (segment.activeSelf != active)
|
||
{
|
||
segment.SetActive(active);
|
||
}
|
||
|
||
if (!active)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
Vector2 point1 = CalculatePointPosition(i, dataPoints[i], width, height);
|
||
Vector2 point2 = CalculatePointPosition(i + 1, dataPoints[i + 1], width, height);
|
||
UpdateLineSegment(segment, point1, point2);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算数据点在图表中的位置
|
||
/// </summary>
|
||
private Vector2 CalculatePointPosition(int index, float value, float width, float height)
|
||
{
|
||
float x = (float)index / (maxDataPoints - 1) * width - width / 2;
|
||
float y = Mathf.Lerp(-height / 2, height / 2,
|
||
Mathf.InverseLerp(minValue, maxValue, value));
|
||
|
||
return new Vector2(x, y);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建线段
|
||
/// </summary>
|
||
private void EnsureLineSegmentCount(int requiredCount)
|
||
{
|
||
if (linePrefab == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
while (lineSegments.Count < requiredCount)
|
||
{
|
||
GameObject segment = Instantiate(linePrefab, lineParent);
|
||
segment.SetActive(false);
|
||
lineSegments.Add(segment);
|
||
}
|
||
}
|
||
|
||
private void UpdateLineSegment(GameObject segment, Vector2 point1, Vector2 point2)
|
||
{
|
||
if (segment == null) return;
|
||
|
||
RectTransform segmentRect = segment.GetComponent<RectTransform>();
|
||
Image segmentImage = segment.GetComponent<Image>();
|
||
if (segmentRect == null) return;
|
||
|
||
// 计算线段的位置、长度和角度
|
||
Vector2 direction = point2 - point1;
|
||
float length = direction.magnitude;
|
||
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
|
||
|
||
// 设置线段位置(中心点)
|
||
segmentRect.anchoredPosition = (point1 + point2) / 2;
|
||
|
||
// 设置线段大小(宽度为lineWidth,高度为长度)
|
||
segmentRect.sizeDelta = new Vector2(length, lineWidth);
|
||
|
||
// 设置线段旋转
|
||
segmentRect.rotation = Quaternion.Euler(0, 0, angle);
|
||
|
||
// 设置颜色
|
||
if (segmentImage != null)
|
||
{
|
||
segmentImage.color = lineColor;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除所有线段
|
||
/// </summary>
|
||
private void ClearLineSegments()
|
||
{
|
||
foreach (var segment in lineSegments)
|
||
{
|
||
if (segment != null)
|
||
{
|
||
Destroy(segment);
|
||
}
|
||
}
|
||
lineSegments.Clear();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新数值标签
|
||
/// </summary>
|
||
private void UpdateValueLabel(float value)
|
||
{
|
||
if (valueLabel != null)
|
||
{
|
||
valueLabel.text = value.ToString("F1") + unit;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置图表标题
|
||
/// </summary>
|
||
public void SetTitle(string title)
|
||
{
|
||
if (titleLabel != null)
|
||
{
|
||
titleLabel.text = title;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置线条颜色
|
||
/// </summary>
|
||
public void SetLineColor(Color color)
|
||
{
|
||
lineColor = color;
|
||
|
||
// 更新现有线段的颜色
|
||
foreach (var segment in lineSegments)
|
||
{
|
||
if (segment != null)
|
||
{
|
||
var image = segment.GetComponent<Image>();
|
||
if (image != null)
|
||
{
|
||
image.color = lineColor;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置值范围
|
||
/// </summary>
|
||
public void SetValueRange(float min, float max)
|
||
{
|
||
minValue = min;
|
||
maxValue = max;
|
||
UpdateChart(); // 重新绘制图表以应用新范围
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清空数据
|
||
/// </summary>
|
||
public void ClearData()
|
||
{
|
||
dataPoints.Clear();
|
||
ClearLineSegments();
|
||
|
||
if (valueLabel != null)
|
||
{
|
||
valueLabel.text = "0.0" + unit;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前数据点数量
|
||
/// </summary>
|
||
public int GetDataPointCount()
|
||
{
|
||
return dataPoints.Count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取最新的数据值
|
||
/// </summary>
|
||
public float GetLatestValue()
|
||
{
|
||
return dataPoints.Count > 0 ? dataPoints[dataPoints.Count - 1] : 0f;
|
||
}
|
||
|
||
void OnDestroy()
|
||
{
|
||
ClearLineSegments();
|
||
}
|
||
}
|