DCS/ruiyiweiUX/Assets/Scripts/UI/UIChartComponent.cs

297 lines
7.5 KiB
C#
Raw Normal View History

2026-06-09 13:59:11 +08:00
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();
}
}