using System.Collections.Generic; using EasyChart; using EasyChart.UGUI; using UnityEngine; public class URadarChartController : MonoBehaviour { private const int AxisCount = 6; [Header("Chart")] public UGUIChartBridge chartBridge; public bool rebuildOnEnable = true; public bool showReferenceSeries = false; public int enforcedSortOrder = 0; [Header("Series Names")] public string mainSeriesName = "Player"; public string referenceSeriesName = "Reference"; [Header("Axes")] public List axes = new List { 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("Style")] public bool overrideSeriesStyle = true; 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; private ChartProfile sourceProfile; private ChartProfile runtimeProfile; private ChartTheme runtimeTheme; private bool rebuildQueued; private void Awake() { showReferenceSeries = false; NormalizeAxes(); QueueRebuild(); } private void OnEnable() { showReferenceSeries = false; NormalizeAxes(); if (rebuildOnEnable) { QueueRebuild(); } } private void LateUpdate() { if (rebuildQueued) { TryRebuild(); } } private void OnValidate() { showReferenceSeries = false; NormalizeAxes(); axisLabelFontSize = Mathf.Max(1, axisLabelFontSize); gridLineWidth = Mathf.Max(0.1f, gridLineWidth); QueueRebuild(); } private void OnDestroy() { if (runtimeProfile != null) { DestroyImmediate(runtimeProfile); runtimeProfile = null; } if (runtimeTheme != null) { DestroyImmediate(runtimeTheme); runtimeTheme = null; } } 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(); } 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() { if (chartBridge == null || chartBridge.Profile == null) { return; } if (sourceProfile == null) { sourceProfile = chartBridge.Profile; } if (sourceProfile == null) { return; } EnsureRuntimeProfile(); EnsureBridgePriority(); ConfigureRuntimeProfile(); chartBridge.Refresh(); ApplyRuntimeTheme(); rebuildQueued = false; } private void EnsureRuntimeProfile() { if (runtimeProfile != null) { if (!ReferenceEquals(chartBridge.Profile, runtimeProfile)) { chartBridge.Profile = runtimeProfile; } return; } runtimeProfile = Instantiate(sourceProfile); runtimeProfile.name = sourceProfile.name + "_URadar_Runtime"; runtimeProfile.hideFlags = HideFlags.DontSave; chartBridge.Profile = runtimeProfile; } private void EnsureBridgePriority() { if (chartBridge != null && chartBridge.SortOrder < enforcedSortOrder) { chartBridge.SortOrder = enforcedSortOrder; } } private void ConfigureRuntimeProfile() { NormalizeAxes(); runtimeProfile.coordinateSystem = CoordinateSystemType.Polar2D; runtimeProfile.EnsureRuntimeData(); runtimeProfile.polarAxes.angleAxis.labels = BuildLabels(); runtimeProfile.polarAxes.angleAxis.visible = true; runtimeProfile.polarAxes.angleAxis.showLabels = true; if (runtimeProfile.polarAxes.angleAxis.labelStyle == null) { runtimeProfile.polarAxes.angleAxis.labelStyle = new LabelStyleSettings(); } runtimeProfile.polarAxes.angleAxis.labelStyle.enabled = true; runtimeProfile.polarAxes.radiusAxis.visible = true; runtimeProfile.polarAxes.radiusAxis.autoRangeMin = false; runtimeProfile.polarAxes.radiusAxis.autoRangeMax = false; runtimeProfile.polarAxes.radiusAxis.minValue = minValue; runtimeProfile.polarAxes.radiusAxis.maxValue = Mathf.Max(minValue + 1f, maxValue); runtimeProfile.polarAxes.radiusAxis.splitCount = Mathf.Max(2, splitCount); if (runtimeProfile.polarAxes.radiusAxis.labelStyle == null) { runtimeProfile.polarAxes.radiusAxis.labelStyle = new LabelStyleSettings(); } runtimeProfile.polarAxes.radiusAxis.labelStyle.enabled = runtimeProfile.polarAxes.radiusAxis.showLabels; if (overrideGridStyle) { runtimeProfile.polarAxes.angleAxis.color = gridLineColor; runtimeProfile.polarAxes.angleAxis.width = gridLineWidth; runtimeProfile.polarAxes.radiusAxis.color = gridLineColor; runtimeProfile.polarAxes.radiusAxis.edgeColor = outerGridLineColor; runtimeProfile.polarAxes.radiusAxis.width = gridLineWidth; } if (overrideLabelStyle) { runtimeProfile.polarAxes.angleAxis.labelColor = axisLabelColor; runtimeProfile.polarAxes.angleAxis.fontSize = axisLabelFontSize; runtimeProfile.polarAxes.angleAxis.labelOffset = axisLabelOffset; runtimeProfile.polarAxes.angleAxis.labelStyle.fontSize = axisLabelFontSize; runtimeProfile.polarAxes.angleAxis.labelStyle.color = axisLabelColor; runtimeProfile.polarAxes.angleAxis.labelStyle.offset = axisLabelOffset; runtimeProfile.polarAxes.radiusAxis.labelColor = axisLabelColor; runtimeProfile.polarAxes.radiusAxis.fontSize = axisLabelFontSize; runtimeProfile.polarAxes.radiusAxis.labelStyle.fontSize = axisLabelFontSize; runtimeProfile.polarAxes.radiusAxis.labelStyle.color = axisLabelColor; } if (runtimeProfile.series == null) { runtimeProfile.series = new List(); } int targetSeriesCount = 1; while (runtimeProfile.series.Count < targetSeriesCount) { runtimeProfile.series.Add(new Serie()); } while (runtimeProfile.series.Count > targetSeriesCount) { runtimeProfile.series.RemoveAt(runtimeProfile.series.Count - 1); } ConfigureSeries(runtimeProfile.series[0], mainSeriesName, true); runtimeProfile.EnsureRuntimeData(); } private void ApplyRuntimeTheme() { if (chartBridge == null || chartBridge.ChartElement == null) { return; } if (!overrideLabelFont || axisLabelFont == null) { chartBridge.ChartElement.Theme = null; return; } if (runtimeTheme == null) { runtimeTheme = ScriptableObject.CreateInstance(); runtimeTheme.name = "URadar_RuntimeTheme"; runtimeTheme.hideFlags = HideFlags.DontSave; } runtimeTheme.primaryFont = axisLabelFont; runtimeTheme.axisFontSize = axisLabelFontSize; chartBridge.ChartElement.Theme = runtimeTheme; } private void ConfigureSeries(Serie serie, string serieName, bool isMainSeries) { if (serie == null) { return; } serie.name = string.IsNullOrWhiteSpace(serieName) ? (isMainSeries ? "Player" : "Reference") : serieName.Trim(); serie.visible = true; if (serie.type != SerieType.Radar) { serie.SetType(SerieType.Radar); } if (!(serie.settings is RadarSettings radarSettings)) { radarSettings = new RadarSettings(); serie.settings = radarSettings; } radarSettings.radar.innerRadius = 0f; radarSettings.radar.outerRadius = 0f; radarSettings.radar.plot.padding = radarPlotPadding; radarSettings.area.show = true; radarSettings.point.show = true; if (overrideSeriesStyle) { radarSettings.stroke.color = isMainSeries ? mainStrokeColor : referenceStrokeColor; radarSettings.stroke.width = isMainSeries ? 2.5f : 2f; radarSettings.area.textureFill.color = isMainSeries ? mainFillColor : referenceFillColor; radarSettings.point.textureFill.color = isMainSeries ? mainPointColor : referencePointColor; radarSettings.point.size = isMainSeries ? 8f : 7f; } if (serie.labelSettings != null) { serie.labelSettings.enabled = false; } if (serie.seriesData == null) { serie.seriesData = new List(); } else { serie.seriesData.Clear(); } for (int i = 0; i < AxisCount; i++) { URadarAxisEntry axis = axes[i]; float value = isMainSeries ? axis.value : axis.referenceValue; serie.seriesData.Add(new SeriesData { id = (isMainSeries ? "main_" : "ref_") + i, name = axis.label, x = i, value = Mathf.Clamp(value, minValue, maxValue) }); } } private List BuildLabels() { List labels = new List(AxisCount); for (int i = 0; i < AxisCount; i++) { string label = axes[i] != null ? axes[i].label : null; labels.Add(string.IsNullOrWhiteSpace(label) ? $"Axis {i + 1}" : label.Trim()); } return labels; } private void NormalizeAxes() { if (axes == null) { axes = new List(); } 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 }; } } } }