2022-03-19 14:53:20 -04:00

309 lines
8.7 KiB
C#

using Fusion;
using UnityEngine;
using UnityEngine.UI;
using Stats = Fusion.Simulation.Statistics;
using Fusion.StatsInternal;
public class FusionStatsMeterBar : FusionGraphBase
{
//public float WarningValueThreshold;
//public float ErrorValueThreshold;
public float HoldPeakTime = 0.1f;
public float DecayTime = 0.25f;
/// <summary>
/// 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 occurance.
/// </summary>
[InlineHelp]
public int MeterMax = 0;
/// <summary>
/// Exposes the UI labels and controls of <see cref="FusionGraph"/>, so they may be modified if customizing this graph.
/// </summary>
[InlineHelp]
[SerializeField]
bool _showUITargets;
[DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)]
public Text ValueLabel;
[DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)]
public Image Bar;
//public string CurrentLabel;
//double _currentRawValue;
double _currentDisplayValue;
double _currentBarValue;
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 = true;
}
}
#endif
public override void Initialize() {
base.Initialize();
_max = MeterMax;
// Prefabs lose editor generated sprites - recreate as needed.
if (BackImage.sprite == null) {
BackImage.sprite = FusionStatsUtilities.MeterSprite;
Bar.sprite = BackImage.sprite;
}
// TODO: Can remove these later. Backwards compat for mask removal on Dec 30 2021
BackImage.type = Image.Type.Simple;
if (Bar.rectTransform.parent != BackImage.rectTransform.parent) {
var oldMask = Bar.transform.parent;
Bar.rectTransform.SetParent(BackImage.rectTransform.parent);
Bar.transform.SetSiblingIndex(BackImage.transform.GetSiblingIndex() + 1);
//Destroy(oldMask);
}
Bar.type = Image.Type.Filled;
Bar.fillMethod = Image.FillMethod.Horizontal;
Bar.fillAmount = 0;
}
double _lastImportedSampleTickTime;
double _max;
double _total;
float _lastPeakSetTime;
public override void Refresh() {
if (_layoutDirty) {
CalculateLayout();
}
var statsBuffer = StatsBuffer;
if (statsBuffer == null || statsBuffer.Count < 1) {
return;
}
// Awkward temp RPC handling
if (statsBuffer.DefaultVisualization == FusionGraphVisualization.CountHistogram) {
if (statsBuffer.Count > 0) {
int highestRpcsFoundForTick = 0;
float newestSampleTick = statsBuffer.GetSampleAtIndex(statsBuffer.Count - 1).TickValue;
var tick = newestSampleTick;
// Only look back at ticks we have not yet already looked at on previous updates.
if (newestSampleTick > _lastImportedSampleTickTime) {
int tickRpcCount = 0;
for (int i = statsBuffer.Count - 1; i >= 0; i--) {
var sampletick = statsBuffer.GetSampleAtIndex(i).TickValue;
if (sampletick > _lastImportedSampleTickTime) {
// If we are now looking at samples from a different tick that previous for loop, reset to get count for this tick now.
if (sampletick != tick) {
tick = sampletick;
// Capture the RPC count for the last recorded tick if it is the new high.
if (tickRpcCount > highestRpcsFoundForTick) {
highestRpcsFoundForTick = tickRpcCount;
}
tickRpcCount = 0;
}
tickRpcCount++;
_total++;
} else {
break;
}
}
_lastImportedSampleTickTime = newestSampleTick;
}
SetValue(highestRpcsFoundForTick);
}
return;
}
if (statsBuffer.Count > 0) {
var value = statsBuffer.GetSampleAtIndex(statsBuffer.Count - 1);
if (value.TickValue == _fusionStats.Runner.Simulation.LatestServerState.Tick) {
SetValue(value.FloatValue);
} else {
SetValue(0);
}
}
}
public void LateUpdate() {
if (DecayTime <= 0) {
return;
}
if (_currentBarValue <= 0) {
return;
}
if (Time.time < _lastPeakSetTime + HoldPeakTime) {
return;
}
double decayedVal = System.Math.Max(_currentBarValue - Time.deltaTime / DecayTime * _max, 0);
SetBar(decayedVal);
}
public void SetValue(double rawvalue) {
var info = StatSourceInfo;
double multiplied = rawvalue * info.Multiplier;
if (MeterMax == 0) {
if (multiplied > _max) {
_max = multiplied;
}
}
double clampedValue = System.Math.Max(System.Math.Min(multiplied, _max), 0);
var roundedValue = System.Math.Round(clampedValue, info.Decimals);
var newDisplayValue = _total > 0 ? _total : roundedValue;
if (clampedValue >= _currentBarValue) {
_lastPeakSetTime = Time.time;
}
if (newDisplayValue != _currentDisplayValue) {
ValueLabel.text = _total > 0 ? _total.ToString() : clampedValue.ToString();
_currentDisplayValue = newDisplayValue;
}
// Only set values greater than the current shown value when using decay.
if (DecayTime >= 0 && clampedValue <= _currentBarValue) {
return;
}
if (clampedValue != _currentBarValue) {
SetBar(clampedValue);
}
}
void SetBar(double value) {
var fusionStats = _fusionStats;
Bar.fillAmount = (float)(value / _max);
_currentBarValue = value;
if (value < WarnThreshold) {
var GoodColor = fusionStats.GraphColorGood;
if (CurrentColor != GoodColor) {
CurrentColor = GoodColor;
Bar.color = GoodColor;
}
} else if (value < ErrorThreshold) {
var WarnColor = fusionStats.GraphColorWarn;
if (CurrentColor != WarnColor) {
Bar.color = WarnColor;
CurrentColor = WarnColor;
}
} else {
var ErrorColor = fusionStats.GraphColorBad;
if (CurrentColor != ErrorColor) {
Bar.color = ErrorColor;
CurrentColor = ErrorColor;
}
}
}
public override void CalculateLayout() {
_layoutDirty = false;
// Special padding handling because Arial vertically sits below center.
var pad = LabelTitle.transform.parent.GetComponent<RectTransform>().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,
Stats.StatSourceTypes statSourceType,
int statId,
float warnThreshold,
float alertThreshold
) {
var info = Stats.GetDescription(statSourceType, statId);
var barRT = parent.CreateRectTransform(info.LongName, true);
var bar = barRT.gameObject.AddComponent<FusionStatsMeterBar>();
bar.StatSourceInfo = info;
bar._fusionStats = fusionStats;
//bar.WarningValueThreshold = warnThreshold;
//bar.ErrorValueThreshold = alertThreshold;
bar._statSourceType = statSourceType;
bar._statId = statId;
bar.GenerateMeter();
return bar;
}
public void GenerateMeter() {
var info = Stats.GetDescription(_statSourceType, _statId);
var backRT = transform.CreateRectTransform("Back", true);
BackImage = backRT.gameObject.AddComponent<Image>();
BackImage.raycastTarget = false;
BackImage.sprite = FusionStatsUtilities.MeterSprite;
BackImage.color = BackColor;
BackImage.type = Image.Type.Simple;
var barRT = transform.CreateRectTransform("Bar", true);
Bar = barRT.gameObject.AddComponent<Image>();
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.LongName, TextAnchor.MiddleLeft, _fusionStats.FontColor);
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("0.0", TextAnchor.MiddleRight, _fusionStats.FontColor);
ValueLabel.alignByGeometry = false;
}
}