Files
bansonic_beta_main/Assets/playerInfoDisplay/uRader/URadarChartController.cs
T
2026-06-30 21:21:30 +08:00

669 lines
21 KiB
C#

using System.Collections.Generic;
using EasyChart.UGUI;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
using XCharts.Runtime;
public class URadarChartController : MonoBehaviour
{
[Header("Legacy Compatibility")]
public UGUIChartBridge chartBridge;
[Header("Chart")]
public bool rebuildOnEnable = true;
public bool showReferenceSeries = false;
public int enforcedSortOrder = 0;
[Header("Axis Count")]
[Min(3)] public int axisCount = 6;
[Header("Series Names")]
public string mainSeriesName = "Player";
public string referenceSeriesName = "Reference";
[Header("Axes")]
public List<URadarAxisEntry> axes = new List<URadarAxisEntry>
{
new URadarAxisEntry { label = "A", value = 72f, referenceValue = 60f },
new URadarAxisEntry { label = "B", value = 55f, referenceValue = 60f },
new URadarAxisEntry { label = "C", value = 68f, referenceValue = 60f },
new URadarAxisEntry { label = "D", value = 49f, referenceValue = 60f },
new URadarAxisEntry { label = "E", value = 80f, referenceValue = 60f },
new URadarAxisEntry { label = "F", value = 61f, referenceValue = 60f }
};
[Header("Range")]
public float minValue = 0f;
public float maxValue = 100f;
public int splitCount = 5;
[Header("Grid")]
public bool overrideGridStyle = false;
public Color gridLineColor = new Color32(255, 255, 255, 180);
public Color outerGridLineColor = Color.white;
public float gridLineWidth = 2f;
[Header("Labels")]
public bool overrideLabelStyle = false;
public bool overrideLabelFont = false;
public Font axisLabelFont;
public int axisLabelFontSize = 18;
public Color axisLabelColor = Color.white;
public Vector2 axisLabelOffset = Vector2.zero;
[Header("Tooltip")]
public bool overrideTooltipStyle = false;
public bool overrideTooltipFont = false;
public Font tooltipFont;
public int tooltipFontSize = 18;
public Color tooltipTextColor = Color.white;
public Color tooltipBackgroundColor = new Color(0f, 0f, 0f, 0.85f);
public Color tooltipBorderColor = new Color(1f, 1f, 1f, 0f);
[Min(0f)] public float tooltipBorderWidth = 0f;
[Min(0f)] public float tooltipCornerRadius = 4f;
public Vector4 tooltipPadding = new Vector4(8f, 8f, 4f, 4f);
[Header("Style")]
public bool overrideSeriesStyle = true;
public bool overrideSymbolStyle = true;
public float mainSymbolSize = 8f;
public float referenceSymbolSize = 7f;
public SymbolType mainSymbolType = SymbolType.Circle;
public SymbolType referenceSymbolType = SymbolType.Circle;
public Color mainStrokeColor = new Color32(82, 126, 255, 255);
public Color mainFillColor = new Color(82f / 255f, 126f / 255f, 255f / 255f, 0.28f);
public Color mainPointColor = Color.white;
public Color referenceStrokeColor = new Color32(72, 229, 229, 255);
public Color referenceFillColor = new Color(72f / 255f, 229f / 255f, 229f / 255f, 0.22f);
public Color referencePointColor = Color.white;
public float radarPlotPadding = 8f;
[Min(0f)] public float radarInnerRadius = 0f;
[Min(0f)] public float radarLabelRadialOffset = 0f;
[Header("Legacy Serialized Fields")]
[SerializeField] private UIRadarGraphic radarGraphic;
[SerializeField] private RectTransform labelRoot;
[SerializeField] private bool autoCreateLabelRoot = true;
[SerializeField] private string labelRootName = "RadarLabels";
[Header("XCharts Runtime")]
[SerializeField] private RectTransform radarChartHost;
[SerializeField] private string radarChartHostName = "RadarChartHost";
[SerializeField] private RadarChart radarChart;
private bool rebuildQueued;
private bool chartInitialized;
public bool IsRendererReady
{
get
{
EnsureRenderer();
return radarChart != null;
}
}
private void Awake()
{
showReferenceSeries = false;
NormalizeAxes();
EnsureRenderer();
QueueRebuild();
}
private void OnEnable()
{
showReferenceSeries = false;
NormalizeAxes();
EnsureRenderer();
if (rebuildOnEnable)
{
QueueRebuild();
}
}
private void LateUpdate()
{
if (rebuildQueued)
{
TryRebuild();
}
}
private void OnValidate()
{
showReferenceSeries = false;
NormalizeAxes();
axisLabelFontSize = Mathf.Max(1, axisLabelFontSize);
tooltipFontSize = Mathf.Max(1, tooltipFontSize);
gridLineWidth = Mathf.Max(0.1f, gridLineWidth);
radarInnerRadius = Mathf.Max(0f, radarInnerRadius);
radarLabelRadialOffset = Mathf.Max(0f, radarLabelRadialOffset);
mainSymbolSize = Mathf.Max(0f, mainSymbolSize);
referenceSymbolSize = Mathf.Max(0f, referenceSymbolSize);
tooltipBorderWidth = Mathf.Max(0f, tooltipBorderWidth);
tooltipCornerRadius = Mathf.Max(0f, tooltipCornerRadius);
axisCount = Mathf.Max(3, axisCount);
EnsureRenderer(false);
QueueRebuild();
}
public void QueueRebuild()
{
rebuildQueued = true;
}
[ContextMenu("Rebuild Radar Chart")]
public void RebuildNow()
{
QueueRebuild();
TryRebuild();
}
public void SetAxisLabel(int index, string label)
{
if (!TryGetAxis(index, out URadarAxisEntry axis))
{
return;
}
axis.label = string.IsNullOrWhiteSpace(label) ? $"Axis {index + 1}" : label.Trim();
QueueRebuild();
}
public void SetAxisValue(int index, float value)
{
if (!TryGetAxis(index, out URadarAxisEntry axis))
{
return;
}
axis.value = Mathf.Clamp(value, minValue, maxValue);
QueueRebuild();
}
public void SetReferenceAxisValue(int index, float value)
{
if (!TryGetAxis(index, out URadarAxisEntry axis))
{
return;
}
axis.referenceValue = Mathf.Clamp(value, minValue, maxValue);
QueueRebuild();
}
public void SetAxisData(int index, string label, float value, float referenceValueToSet)
{
NormalizeAxes();
if (index < 0 || index >= axes.Count)
{
return;
}
URadarAxisEntry axis = axes[index];
if (axis == null)
{
axis = new URadarAxisEntry();
axes[index] = axis;
}
axis.label = string.IsNullOrWhiteSpace(label) ? $"Axis {index + 1}" : label.Trim();
axis.value = Mathf.Clamp(value, minValue, maxValue);
axis.referenceValue = Mathf.Clamp(referenceValueToSet, minValue, maxValue);
QueueRebuild();
}
public void NormalizeAxesForExternalUse()
{
NormalizeAxes();
}
public void EnsureRenderer(bool initializeChart = true)
{
DisableLegacyRenderer();
EnsureChartHost();
if (radarChart == null)
{
radarChart = radarChartHost != null ? radarChartHost.GetComponent<RadarChart>() : null;
}
if (radarChart == null && radarChartHost != null)
{
radarChart = radarChartHost.gameObject.AddComponent<RadarChart>();
}
if (radarChart != null)
{
radarChart.enabled = true;
radarChart.gameObject.SetActive(true);
}
if (initializeChart)
{
EnsureChartInitialized();
}
EnsureRuntimeChartStyle();
}
private bool TryGetAxis(int index, out URadarAxisEntry axis)
{
NormalizeAxes();
if (index < 0 || index >= axes.Count)
{
axis = null;
return false;
}
axis = axes[index];
return axis != null;
}
private void TryRebuild()
{
EnsureRenderer();
if (radarChart == null)
{
return;
}
ApplyRendererState();
rebuildQueued = false;
}
private void ApplyRendererState()
{
NormalizeAxes();
EnsureChartInitialized();
radarChart.RemoveChartComponents<RadarCoord>();
radarChart.RemoveData();
RadarCoord radarCoord = radarChart.AddChartComponent<RadarCoord>();
if (radarCoord == null)
{
return;
}
radarCoord.indicatorList.Clear();
float clampedMin = Mathf.Min(minValue, maxValue - 0.0001f);
float clampedMax = Mathf.Max(minValue + 0.0001f, maxValue);
for (int i = 0; i < axes.Count; i++)
{
string label = string.IsNullOrWhiteSpace(axes[i].label) ? $"Axis {i + 1}" : axes[i].label.Trim();
radarCoord.AddIndicator(label, clampedMin, clampedMax);
}
ConfigureRadarCoord(radarCoord);
Radar mainSerie = radarChart.AddSerie<Radar>(mainSeriesName);
if (mainSerie != null)
{
mainSerie.radarIndex = radarCoord.index;
}
ConfigureSerie(mainSerie, true);
if (mainSerie != null)
{
radarChart.AddData(mainSerie.index, BuildDataValues(false), mainSeriesName);
}
if (showReferenceSeries)
{
Radar referenceSerie = radarChart.AddSerie<Radar>(referenceSeriesName);
if (referenceSerie != null)
{
referenceSerie.radarIndex = radarCoord.index;
}
ConfigureSerie(referenceSerie, false);
if (referenceSerie != null)
{
radarChart.AddData(referenceSerie.index, BuildDataValues(true), referenceSeriesName);
}
}
RefreshLegendAndTooltip();
radarChart.RefreshChart();
}
private void ConfigureRadarCoord(RadarCoord radarCoord)
{
RectTransform rectTransform = transform as RectTransform;
float minRectSize = rectTransform == null ? 0f : Mathf.Min(rectTransform.rect.width, rectTransform.rect.height);
float reservedRadius = Mathf.Max(0f, radarPlotPadding);
float computedRadius = minRectSize > 0f
? Mathf.Max(0f, (minRectSize * 0.5f) - reservedRadius - Mathf.Max(0f, radarLabelRadialOffset))
: 0f;
float xchartsRadius = minRectSize > 0f ? Mathf.Clamp01(computedRadius / minRectSize) : 0.35f;
if (xchartsRadius <= 0f)
{
xchartsRadius = 0.35f;
}
radarCoord.show = true;
radarCoord.shape = RadarCoord.Shape.Polygon;
radarCoord.splitNumber = Mathf.Max(2, splitCount);
radarCoord.center[0] = 0.5f;
radarCoord.center[1] = 0.5f;
radarCoord.radius = xchartsRadius;
radarCoord.indicator = true;
radarCoord.indicatorGap = radarLabelRadialOffset;
radarCoord.positionType = RadarCoord.PositionType.Vertice;
radarCoord.isAxisTooltip = false;
radarCoord.startAngle = 0f;
Color innerGrid = overrideGridStyle ? gridLineColor : new Color32(255, 255, 255, 180);
Color outerGrid = overrideGridStyle ? outerGridLineColor : Color.white;
Color effectiveLabelColor = overrideLabelStyle ? axisLabelColor : Color.white;
Vector2 effectiveLabelOffset = overrideLabelStyle ? axisLabelOffset : Vector2.zero;
Font effectiveLabelFont = overrideLabelFont && axisLabelFont != null ? axisLabelFont : axisLabelFont;
radarCoord.axisLine.show = true;
radarCoord.axisLine.lineStyle.show = true;
radarCoord.axisLine.lineStyle.type = LineStyle.Type.Solid;
radarCoord.axisLine.lineStyle.width = gridLineWidth;
radarCoord.axisLine.lineStyle.color = innerGrid;
radarCoord.splitLine.show = true;
radarCoord.splitLine.lineStyle.show = true;
radarCoord.splitLine.lineStyle.type = LineStyle.Type.Solid;
radarCoord.splitLine.lineStyle.width = gridLineWidth;
radarCoord.splitLine.lineStyle.color = outerGrid;
radarCoord.splitArea.show = false;
radarCoord.axisName.show = true;
radarCoord.axisName.name = null;
radarCoord.axisName.labelStyle.show = true;
radarCoord.axisName.labelStyle.textStyle.autoColor = false;
radarCoord.axisName.labelStyle.textStyle.color = effectiveLabelColor;
radarCoord.axisName.labelStyle.textStyle.fontSize = axisLabelFontSize;
radarCoord.axisName.labelStyle.textStyle.font = effectiveLabelFont;
radarCoord.axisName.labelStyle.textStyle.autoAlign = false;
radarCoord.axisName.labelStyle.textStyle.alignment = TextAnchor.MiddleCenter;
radarCoord.axisName.labelStyle.offset = new Vector3(effectiveLabelOffset.x, effectiveLabelOffset.y, 0f);
radarCoord.axisName.labelStyle.width = 140f;
radarCoord.axisName.labelStyle.height = Mathf.Max(24f, axisLabelFontSize + 10f);
radarCoord.SetVerticesDirty();
}
private void ConfigureSerie(Radar serie, bool isMainSerie)
{
if (serie == null)
{
return;
}
serie.radarType = RadarType.Multiple;
serie.showDataName = false;
serie.symbol.show = true;
if (overrideSymbolStyle)
{
serie.symbol.type = isMainSerie ? mainSymbolType : referenceSymbolType;
serie.symbol.size = isMainSerie ? mainSymbolSize : referenceSymbolSize;
serie.symbol.color = isMainSerie ? mainPointColor : referencePointColor;
}
else
{
serie.symbol.type = SymbolType.Circle;
serie.symbol.size = isMainSerie ? 8f : 7f;
serie.symbol.color = Color.white;
}
serie.lineStyle.show = true;
serie.lineStyle.type = LineStyle.Type.Solid;
serie.lineStyle.width = isMainSerie ? 2.5f : 2f;
serie.lineStyle.color = isMainSerie
? (overrideSeriesStyle ? mainStrokeColor : new Color32(82, 126, 255, 255))
: (overrideSeriesStyle ? referenceStrokeColor : new Color32(72, 229, 229, 255));
AreaStyle areaStyle = serie.EnsureComponent<AreaStyle>();
areaStyle.show = true;
areaStyle.color = isMainSerie
? (overrideSeriesStyle ? mainFillColor : new Color(82f / 255f, 126f / 255f, 1f, 0.28f))
: (overrideSeriesStyle ? referenceFillColor : new Color(72f / 255f, 229f / 255f, 229f / 255f, 0.22f));
areaStyle.toColor = Color.clear;
areaStyle.opacity = 1f;
LabelStyle labelStyle = serie.EnsureComponent<LabelStyle>();
labelStyle.show = false;
}
private List<double> BuildDataValues(bool useReferenceValue)
{
List<double> values = new List<double>(axes.Count);
for (int i = 0; i < axes.Count; i++)
{
float raw = useReferenceValue ? axes[i].referenceValue : axes[i].value;
values.Add(Mathf.Clamp(raw, minValue, maxValue));
}
return values;
}
private void RefreshLegendAndTooltip()
{
Legend legend = radarChart.GetChartComponent<Legend>();
if (legend != null)
{
legend.show = false;
legend.data.Clear();
}
Title title = radarChart.GetChartComponent<Title>();
if (title != null)
{
title.show = false;
title.text = string.Empty;
title.subText = string.Empty;
}
XCharts.Runtime.Background background = radarChart.GetChartComponent<XCharts.Runtime.Background>();
if (background != null)
{
background.show = false;
}
Tooltip tooltip = radarChart.GetChartComponent<Tooltip>();
if (tooltip == null)
{
return;
}
tooltip.show = false;
tooltip.showContent = false;
tooltip.type = Tooltip.Type.None;
tooltip.trigger = Tooltip.Trigger.None;
if (overrideTooltipStyle)
{
tooltip.backgroundColor = tooltipBackgroundColor;
tooltip.borderColor = tooltipBorderColor;
tooltip.borderWidth = tooltipBorderWidth;
tooltip.paddingLeftRight = Mathf.RoundToInt(Mathf.Max(0f, tooltipPadding.x));
tooltip.paddingTopBottom = Mathf.RoundToInt(Mathf.Max(0f, tooltipPadding.z));
}
if (overrideTooltipFont)
{
tooltip.titleLabelStyle.textStyle.font = tooltipFont;
tooltip.titleLabelStyle.textStyle.fontSize = tooltipFontSize;
tooltip.titleLabelStyle.textStyle.color = tooltipTextColor;
for (int i = 0; i < tooltip.contentLabelStyles.Count; i++)
{
tooltip.contentLabelStyles[i].textStyle.font = tooltipFont;
tooltip.contentLabelStyles[i].textStyle.fontSize = tooltipFontSize;
tooltip.contentLabelStyles[i].textStyle.color = tooltipTextColor;
}
}
}
private void EnsureChartInitialized()
{
if (radarChart == null)
{
return;
}
if (!chartInitialized)
{
radarChart.Init();
chartInitialized = true;
}
else
{
radarChart.EnsureChartComponent<Title>();
radarChart.EnsureChartComponent<Tooltip>();
radarChart.EnsureChartComponent<XCharts.Runtime.Background>();
radarChart.EnsureChartComponent<RadarCoord>();
}
}
private void EnsureRuntimeChartStyle()
{
if (radarChart == null)
{
return;
}
radarChart.raycastTarget = false;
CanvasRenderer canvasRenderer = radarChart.GetComponent<CanvasRenderer>();
if (canvasRenderer != null)
{
canvasRenderer.SetAlpha(1f);
}
}
private void EnsureChartHost()
{
if (radarChartHost == null)
{
Transform existing = transform.Find(radarChartHostName);
if (existing != null)
{
radarChartHost = existing as RectTransform;
}
}
if (radarChartHost == null)
{
GameObject host = new GameObject(radarChartHostName, typeof(RectTransform));
radarChartHost = host.GetComponent<RectTransform>();
radarChartHost.SetParent(transform, false);
}
radarChartHost.anchorMin = Vector2.zero;
radarChartHost.anchorMax = Vector2.one;
radarChartHost.pivot = new Vector2(0.5f, 0.5f);
radarChartHost.anchoredPosition = Vector2.zero;
radarChartHost.sizeDelta = Vector2.zero;
radarChartHost.offsetMin = Vector2.zero;
radarChartHost.offsetMax = Vector2.zero;
radarChartHost.localScale = Vector3.one;
radarChartHost.localRotation = Quaternion.identity;
radarChartHost.SetAsLastSibling();
radarChartHost.gameObject.SetActive(true);
}
private void DisableLegacyRenderer()
{
if (chartBridge == null)
{
chartBridge = GetComponent<UGUIChartBridge>();
}
if (chartBridge != null)
{
chartBridge.enabled = false;
}
UIDocument uiDocument = GetComponent<UIDocument>();
if (uiDocument != null)
{
uiDocument.enabled = false;
}
RawImage rawImage = GetComponent<RawImage>();
if (rawImage != null)
{
rawImage.enabled = false;
rawImage.texture = null;
rawImage.raycastTarget = false;
Color color = rawImage.color;
color.a = 0f;
rawImage.color = color;
}
if (radarGraphic == null)
{
radarGraphic = GetComponent<UIRadarGraphic>();
}
if (radarGraphic != null)
{
radarGraphic.enabled = false;
radarGraphic.raycastTarget = false;
Color color = radarGraphic.color;
color.a = 0f;
radarGraphic.color = color;
radarGraphic.SetVerticesDirty();
}
if (labelRoot == null && autoCreateLabelRoot)
{
Transform existing = transform.Find(labelRootName);
if (existing != null)
{
labelRoot = existing as RectTransform;
}
}
if (labelRoot != null)
{
labelRoot.gameObject.SetActive(false);
}
}
private void NormalizeAxes()
{
if (axes == null)
{
axes = new List<URadarAxisEntry>();
}
axisCount = Mathf.Max(3, axisCount);
while (axes.Count < axisCount)
{
axes.Add(new URadarAxisEntry
{
label = $"Axis {axes.Count + 1}",
value = 50f,
referenceValue = 60f
});
}
if (axes.Count > axisCount)
{
axes.RemoveRange(axisCount, axes.Count - axisCount);
}
for (int i = 0; i < axes.Count; i++)
{
if (axes[i] == null)
{
axes[i] = new URadarAxisEntry
{
label = $"Axis {i + 1}",
value = 50f,
referenceValue = 60f
};
}
}
}
}