498 lines
16 KiB
C#
498 lines
16 KiB
C#
using System.Collections.Generic;
|
|
using EasyChart;
|
|
using EasyChart.UGUI;
|
|
using UnityEngine;
|
|
|
|
public class URadarChartController : MonoBehaviour
|
|
{
|
|
[Header("Chart")]
|
|
public UGUIChartBridge chartBridge;
|
|
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 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;
|
|
|
|
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);
|
|
tooltipFontSize = Mathf.Max(1, tooltipFontSize);
|
|
gridLineWidth = Mathf.Max(0.1f, gridLineWidth);
|
|
radarInnerRadius = Mathf.Max(0f, radarInnerRadius);
|
|
radarLabelRadialOffset = Mathf.Max(0f, radarLabelRadialOffset);
|
|
tooltipBorderWidth = Mathf.Max(0f, tooltipBorderWidth);
|
|
tooltipCornerRadius = Mathf.Max(0f, tooltipCornerRadius);
|
|
axisCount = Mathf.Max(3, axisCount);
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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<Serie>();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool useTooltipStyle = overrideTooltipStyle;
|
|
bool useTooltipFont = overrideTooltipFont && tooltipFont != null;
|
|
bool useLabelFont = overrideLabelFont && axisLabelFont != null;
|
|
|
|
if (!useLabelFont && !useTooltipFont && !useTooltipStyle)
|
|
{
|
|
chartBridge.ChartElement.Theme = null;
|
|
return;
|
|
}
|
|
|
|
if (runtimeTheme == null)
|
|
{
|
|
runtimeTheme = ScriptableObject.CreateInstance<ChartTheme>();
|
|
runtimeTheme.name = "URadar_RuntimeTheme";
|
|
runtimeTheme.hideFlags = HideFlags.DontSave;
|
|
}
|
|
|
|
runtimeTheme.primaryFont = useLabelFont ? axisLabelFont : null;
|
|
runtimeTheme.axisFontSize = useLabelFont ? axisLabelFontSize : -1f;
|
|
runtimeTheme.tooltipFont = useTooltipFont ? tooltipFont : null;
|
|
runtimeTheme.tooltipFontSize = (useTooltipFont || useTooltipStyle) ? tooltipFontSize : -1f;
|
|
runtimeTheme.tooltipTextColor = useTooltipStyle ? tooltipTextColor : Color.white;
|
|
runtimeTheme.tooltipBackgroundColor = useTooltipStyle ? tooltipBackgroundColor : new Color(0f, 0f, 0f, 0.8f);
|
|
runtimeTheme.tooltipBorderColor = useTooltipStyle ? tooltipBorderColor : new Color(0f, 0f, 0f, 0f);
|
|
runtimeTheme.tooltipBorderWidth = useTooltipStyle ? tooltipBorderWidth : 0f;
|
|
runtimeTheme.tooltipCornerRadius = useTooltipStyle ? tooltipCornerRadius : 4f;
|
|
runtimeTheme.tooltipPadding = useTooltipStyle ? tooltipPadding : new Vector4(8f, 8f, 4f, 4f);
|
|
|
|
// Force ChartElement.Theme setter to re-apply tooltip visuals even when reusing the same runtime theme instance.
|
|
chartBridge.ChartElement.Theme = null;
|
|
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 = radarInnerRadius;
|
|
radarSettings.radar.outerRadius = 0f;
|
|
radarSettings.radar.plot.padding = radarPlotPadding;
|
|
radarSettings.radar.plot.labelRadialOffset = radarLabelRadialOffset;
|
|
|
|
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<SeriesData>();
|
|
}
|
|
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<string> BuildLabels()
|
|
{
|
|
List<string> labels = new List<string>(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<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
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|