namespace Fusion {
using UnityEngine;
using UnityEngine.UI;
//using Stats = Fusion.Simulation.Statistics;
using Fusion.StatsInternal;
public class FusionStatsMeterBar : FusionStatsGraphBase {
public float HoldPeakTime = 0.1f;
public float DecayTime = 0.25f;
///
/// Values greater than 0 will limit the meter to a range of 0 to MeterMax.
/// Value of 0 will adjust the max to the largest value occurrence.
///
[InlineHelp]
public int MeterMax; // = 1200;
///
/// Exposes the UI labels and controls of , so they may be modified if customizing this graph.
///
[InlineHelp]
[SerializeField]
bool _showUITargets;
[DrawIf(nameof(_showUITargets), Hide = true)]
public Text ValueLabel;
[DrawIf(nameof(_showUITargets), Hide = true)]
public Image Bar;
[DrawIf(nameof(_showUITargets), Hide = true)]
public Image BarPeak;
double _currentDisplayValue;
double _currentRangeMax;
double _currentBarValue;
double _currentPeakValue;
Color CurrentColor;
protected override Color BackColor => base.BackColor * new Color(.5f, .5f, .5f, 1);
#if UNITY_EDITOR
protected override void OnValidate() {
base.OnValidate();
if (MeterMax < 0) {
MeterMax = 0;
}
if (Application.isPlaying == false) {
TryConnect();
_layoutDirty = _layoutDirty < 1 ? 1 : _layoutDirty;
}
}
#endif
public override void Initialize() {
base.Initialize();
_currentRangeMax = MeterMax;
// Prefabs lose editor generated sprites - recreate as needed.
if (BackImage.sprite == null) {
BackImage.sprite = FusionStatsUtilities.MeterSprite;
Bar.sprite = BackImage.sprite;
BarPeak.sprite = BackImage.sprite;
}
}
double _lastImportedSampleTickTime;
// double _rangeMax;
float _lastBarPeakSetTime;
public override void Refresh() {
if (_layoutDirty > 0) {
CalculateLayout();
}
if (StatSourceInfo == null) {
return;
}
if (StatsObject == null) {
return;
}
var statsBuffer = (OverTimeStatBuffer)FieldInfo.GetValue(StatsObject);
double newestTime = _lastImportedSampleTickTime;
double highestNewValue = 0;
double peakValue = 0;
for(int i = 0; i < statsBuffer.Length; ++i) {
var e = statsBuffer[i];
if (e.Time > _lastImportedSampleTickTime) {
if (e.Value > highestNewValue) {
highestNewValue = e.Value;
}
newestTime = e.Time;
}
// TODO: Use this peak value on graph
if (e.Value > peakValue) {
peakValue = e.Value;
}
_lastImportedSampleTickTime = newestTime;
}
SetValue(highestNewValue, peakValue);
}
public void LateUpdate() {
if (DecayTime <= 0) {
return;
}
if (_currentBarValue <= 0) {
return;
}
if (Time.time < _lastBarPeakSetTime + HoldPeakTime) {
return;
}
double decayedVal = System.Math.Max(_currentBarValue - Time.deltaTime / DecayTime * _currentRangeMax, 0);
SetBar(decayedVal, _currentRangeMax, false);
}
public void SetValue(double rawvalue, double rawPeak) {
var info = StatSourceInfo;
double multipliedValue = rawvalue; // * info.Multiplier;
double multipliedPeak = rawPeak; // * some multiplier
var meterMax = MeterMax;
double rangeMax = _currentRangeMax;
// Clamp value if the Meter is fixed. No need to clamp if not, the meter will always accomodate values.
double clampedValue, clampedPeak;
if (meterMax == 0) {
if (multipliedPeak > rangeMax) {
rangeMax = multipliedPeak;
}
clampedValue = multipliedValue;
clampedPeak = multipliedPeak;
} else {
clampedValue = System.Math.Max(System.Math.Min(multipliedValue, rangeMax), 0);
clampedPeak = System.Math.Max(System.Math.Min(multipliedPeak, rangeMax), 0);
}
var roundedValue = System.Math.Round(clampedValue, info.Decimals);
var roundedPeak = System.Math.Round(clampedPeak, info.Decimals);
// Reset the hold time for the bar once it increases from a previous value.
if (roundedValue >= _currentBarValue) {
_lastBarPeakSetTime = Time.time;
}
// Avoid repaints when nothing has changed.
if (roundedValue == _currentDisplayValue && roundedPeak == _currentPeakValue && rangeMax == _currentRangeMax) {
return;
}
ValueLabel.text = $"{clampedValue} [{clampedPeak} PK]";
_currentDisplayValue = roundedValue;
_currentPeakValue = clampedPeak;
_currentRangeMax = rangeMax;
// Only set values greater than the current shown value when using decay.
if (DecayTime >= 0 && clampedValue <= _currentBarValue) {
return;
}
if (clampedValue != _currentBarValue) {
SetBar(clampedValue, rangeMax, false);
SetBar(clampedPeak, rangeMax, true);
}
}
void SetBar(double value, double rangeMax, bool setPeakBar) {
var bar = setPeakBar ? BarPeak : Bar;
var fusionStats = FusionStats;
bar.fillAmount = (float)(value / rangeMax);
_currentBarValue = value;
if (value < WarnThreshold) {
var GoodColor = setPeakBar ? fusionStats.GraphColorGood * .75f : fusionStats.GraphColorGood;
if (CurrentColor != GoodColor) {
CurrentColor = GoodColor;
bar.color = GoodColor;
}
} else if (value < ErrorThreshold) {
var WarnColor = setPeakBar ? fusionStats.GraphColorWarn * .75f : fusionStats.GraphColorWarn;
if (CurrentColor != WarnColor) {
bar.color = WarnColor;
CurrentColor = WarnColor;
}
} else {
var ErrorColor = setPeakBar ? fusionStats.GraphColorBad * .75f : fusionStats.GraphColorBad;
if (CurrentColor != ErrorColor) {
bar.color = ErrorColor;
CurrentColor = ErrorColor;
}
}
}
public override void CalculateLayout() {
_layoutDirty--;
// Special padding handling because Arial vertically sits below center.
var pad = LabelTitle.transform.parent.GetComponent().rect.height * .2f;
LabelTitle.rectTransform.offsetMax = new Vector2(0, -pad);
LabelTitle.rectTransform.offsetMin = new Vector2(PAD, pad * 1.2f);
ValueLabel.rectTransform.offsetMax = new Vector2(-PAD, -pad);
ValueLabel.rectTransform.offsetMin = new Vector2(0, pad * 1.2f);
ApplyTitleText();
}
// unfinished
public static FusionStatsMeterBar Create(
RectTransform parent,
FusionStats fusionStats,
StatSourceTypes statSourceType,
int statId,
float warnThreshold,
float alertThreshold
) {
var info = statSourceType.GetDescription(statId);
var barRT = parent.CreateRectTransform(info.meta.Name, true);
var bar = barRT.gameObject.AddComponent();
bar._statSourceInfo = info.meta;
bar._fusionStats = fusionStats;
//bar.WarningValueThreshold = warnThreshold;
//bar.ErrorValueThreshold = alertThreshold;
bar._statSourceType = statSourceType;
bar._statId = statId;
bar.GenerateMeter();
return bar;
}
public void GenerateMeter() {
var fusionStats = FusionStats;
var info = _statSourceType.GetDescription(_statId);
var backRT = transform.CreateRectTransform("Back", true);
BackImage = backRT.gameObject.AddComponent();
BackImage.raycastTarget = false;
BackImage.sprite = FusionStatsUtilities.MeterSprite;
BackImage.color = BackColor;
BackImage.type = Image.Type.Simple;
var barPeakRT = transform.CreateRectTransform("BarPeak", true);
BarPeak = barPeakRT.gameObject.AddComponent();
BarPeak.raycastTarget = false;
BarPeak.sprite = BackImage.sprite;
BarPeak.color = fusionStats.GraphColorGood;
BarPeak.type = Image.Type.Filled;
BarPeak.fillMethod = Image.FillMethod.Horizontal;
BarPeak.fillAmount = 0;
var barRT = transform.CreateRectTransform("Bar", true);
Bar = barRT.gameObject.AddComponent();
Bar.raycastTarget = false;
Bar.sprite = BackImage.sprite;
Bar.color = fusionStats.GraphColorGood;
Bar.type = Image.Type.Filled;
Bar.fillMethod = Image.FillMethod.Horizontal;
Bar.fillAmount = 0;
var titleRT = transform.CreateRectTransform("Label", true)
.ExpandAnchor()
.SetAnchors(0.0f, 0.5f, 0.0f, 1.0f)
.SetOffsets(6, -6, 6, -6);
LabelTitle = titleRT.AddText(info.meta.Name, TextAnchor.MiddleLeft, fusionStats.FontColor, fusionStats.LabelFont);
LabelTitle.alignByGeometry = false;
var valueRT = transform.CreateRectTransform("Value", true)
.ExpandAnchor()
.SetAnchors(0.5f, 1.0f, 0.0f, 1.0f)
.SetOffsets(6, -6, 6, -6);
ValueLabel = valueRT.AddText("- [-]", TextAnchor.MiddleRight, fusionStats.FontColor, fusionStats.ValueFont);
ValueLabel.alignByGeometry = false;
}
}
}