You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
9.3 KiB
C#
261 lines
9.3 KiB
C#
1 month ago
|
namespace Fusion.Statistics {
|
||
|
using System;
|
||
|
using UnityEngine;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using UnityEngine.EventSystems;
|
||
|
using UnityEngine.Profiling;
|
||
|
using UnityEngine.Serialization;
|
||
|
|
||
|
[RequireComponent(typeof(NetworkRunner))]
|
||
|
[AddComponentMenu("Fusion/Statistics/Fusion Statistics")]
|
||
|
public class FusionStatistics : SimulationBehaviour, ISpawned {
|
||
|
internal List<FusionStatsGraphBase> ActiveGraphs => _statsGraph;
|
||
|
|
||
|
// Setup prefabs
|
||
|
private GameObject _statsCanvasPrefab;
|
||
|
private FusionNetworkObjectStatsGraphCombine _objectGraphCombinePrefab;
|
||
|
|
||
|
private const string STATS_CANVAS_PREFAB_PATH = "FusionStatsResources/FusionStatsRenderPanel";
|
||
|
private const string STATS_OBJECT_COMBINE_PREFAB_PATH = "FusionStatsResources/NetworkObjectStatistics";
|
||
|
|
||
|
private List<FusionStatsGraphBase> _statsGraph;
|
||
|
private FusionStatsPanelHeader _header;
|
||
|
private FusionStatsConfig _config;
|
||
|
private FusionStatsCanvas _statsCanvas;
|
||
|
private GameObject _statsPanelObject;
|
||
|
private Dictionary<FusionNetworkObjectStatistics, FusionNetworkObjectStatsGraphCombine> _objectStatsGraphCombines;
|
||
|
|
||
|
[InlineHelp]
|
||
|
[ExpandableEnum]
|
||
|
[SerializeField] private RenderSimStats _statsEnabled;
|
||
|
|
||
|
[InlineHelp]
|
||
|
[SerializeField] private CanvasAnchor _canvasAnchor = CanvasAnchor.TopRight;
|
||
|
|
||
|
|
||
|
[FormerlySerializedAs("_statsConfig")] [SerializeField]
|
||
|
[Header("Custom configuration to override default values.\nSelect only one stat flag per configuration.")]
|
||
|
private List<FusionStatisticsStatCustomConfig> _statsCustomConfig = new List<FusionStatisticsStatCustomConfig>();
|
||
|
|
||
|
internal List<FusionStatisticsStatCustomConfig> StatsCustomConfig => _statsCustomConfig;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a value indicating whether the statistics panel is active.
|
||
|
/// </summary>
|
||
|
public bool IsPanelActive => _statsPanelObject != false;
|
||
|
|
||
|
[System.Serializable]
|
||
|
public struct FusionStatisticsStatCustomConfig {
|
||
|
public RenderSimStats Stat;
|
||
|
public float Threshold1;
|
||
|
public float Threshold2;
|
||
|
public float Threshold3;
|
||
|
public bool IgnoreZeroOnBuffer;
|
||
|
public bool IgnoreZeroOnAverageCalculation;
|
||
|
public int AccumulateTimeMs;
|
||
|
}
|
||
|
|
||
|
private void Awake() {
|
||
|
_statsGraph = new List<FusionStatsGraphBase>();
|
||
|
_statsCanvasPrefab = Resources.Load<GameObject>(STATS_CANVAS_PREFAB_PATH);
|
||
|
_objectGraphCombinePrefab = Resources.Load<FusionNetworkObjectStatsGraphCombine>(STATS_OBJECT_COMBINE_PREFAB_PATH);
|
||
|
|
||
|
if (_statsCanvasPrefab == null || _objectGraphCombinePrefab == null) {
|
||
|
Log.Error($"Error loading the required assets for Fusion Statistics, destroying stats instance. Make sure that the following paths are valid for the Fusion Statistics resource assets: \n 1. {STATS_CANVAS_PREFAB_PATH} \n 2. {STATS_OBJECT_COMBINE_PREFAB_PATH}");
|
||
|
Destroy(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ISpawned.Spawned() {
|
||
|
SetupStatisticsPanel();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the custom configuration for Fusion Statistics.
|
||
|
/// </summary>
|
||
|
/// <param name="customConfig">The list of custom configurations for Fusion Statistics.</param>
|
||
|
public void SetStatsCustomConfig(List<FusionStatisticsStatCustomConfig> customConfig) {
|
||
|
if (customConfig == default) {
|
||
|
Log.Warn("Trying to set a null Fusion Statistics custom stats config");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_statsCustomConfig = customConfig;
|
||
|
ApplyCustomConfig();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the anchor position of the Fusion Statistics canvas.
|
||
|
/// </summary>
|
||
|
/// <param name="anchor">The anchor position of the canvas (TopLeft or TopRight).</param>
|
||
|
public void SetCanvasAnchor(CanvasAnchor anchor) {
|
||
|
_canvasAnchor = anchor;
|
||
|
if (_statsCanvas == false) return;
|
||
|
_statsCanvas.SetCanvasAnchor(anchor);
|
||
|
}
|
||
|
|
||
|
private void ApplyCustomConfig() {
|
||
|
if (!_header) return;
|
||
|
_header.ApplyStatsConfig(_statsCustomConfig);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called from a custom editor script.
|
||
|
/// Will update any editor information into the fusion statistics.
|
||
|
/// </summary>
|
||
|
public void OnEditorChange() {
|
||
|
RenderEnabledStats();
|
||
|
ApplyCustomConfig();
|
||
|
SetCanvasAnchor(_canvasAnchor);
|
||
|
}
|
||
|
|
||
|
private void RenderEnabledStats() {
|
||
|
if (IsPanelActive == false) return;
|
||
|
_header.SetStatsToRender(_statsEnabled);
|
||
|
}
|
||
|
|
||
|
internal void UpdateStatsEnabled(RenderSimStats stats) {
|
||
|
_statsEnabled = stats;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets up the statistics panel for Fusion statistic tracking.
|
||
|
/// </summary>
|
||
|
public void SetupStatisticsPanel() {
|
||
|
if (IsPanelActive) return;
|
||
|
|
||
|
// Was not registered on the Runner yet
|
||
|
if (Runner == null) {
|
||
|
var runner = GetComponent<NetworkRunner>();
|
||
|
|
||
|
if (runner.IsRunning == false) {
|
||
|
Log.Warn($"Network Runner on ({runner.gameObject}) is not yet running.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
runner.AddGlobal(this);
|
||
|
// Return because when spawned is called the setup method will be called again.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_objectStatsGraphCombines = new Dictionary<FusionNetworkObjectStatistics, FusionNetworkObjectStatsGraphCombine>();
|
||
|
|
||
|
_statsPanelObject = Instantiate(_statsCanvasPrefab, transform);
|
||
|
_statsCanvas = _statsPanelObject.GetComponentInChildren<FusionStatsCanvas>();
|
||
|
_statsCanvas.SetupStatsCanvas(this, _canvasAnchor, DestroyStatisticsPanel);
|
||
|
_header = _statsPanelObject.GetComponentInChildren<FusionStatsPanelHeader>();
|
||
|
_header.SetupHeader(Runner.LocalPlayer.ToString(), this);
|
||
|
_config = _statsPanelObject.GetComponentInChildren<FusionStatsConfig>(true);
|
||
|
|
||
|
_statsPanelObject.AddComponent<FusionBasicBillboard>();
|
||
|
ApplyCustomConfig();
|
||
|
|
||
|
Runner.AddVisibilityNodes(_statsPanelObject);
|
||
|
|
||
|
if (_statsEnabled != 0)
|
||
|
RenderEnabledStats();
|
||
|
|
||
|
// Setup Event system
|
||
|
if (!EventSystem.current) {
|
||
|
Log.Debug("Fusion Statistics: No event system detected, creating one.");
|
||
|
new GameObject("EventSystem-FusionStatistics", typeof(EventSystem), typeof(StandaloneInputModule));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the world anchor for Fusion Statistics. Set null to return to screen space overlay.
|
||
|
/// </summary>
|
||
|
/// <param name="anchor">The FusionStatsWorldAnchor component that defines the anchor object. Null to return to screen space overlay.</param>
|
||
|
/// <param name="scale">The scale of the statistics panel.</param>
|
||
|
public void SetWorldAnchor(FusionStatsWorldAnchor anchor, float scale) {
|
||
|
_config.SetWorldCanvasScale(scale);
|
||
|
|
||
|
if (anchor == null) {
|
||
|
_config.ResetToCanvasAnchor();
|
||
|
} else {
|
||
|
_config.SetWorldAnchor(anchor.transform);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Destroys the statistics panel.
|
||
|
/// </summary>
|
||
|
public void DestroyStatisticsPanel() {
|
||
|
var keys = _objectStatsGraphCombines?.Keys.ToArray();
|
||
|
if (keys != null) {
|
||
|
foreach (var fusionNetworkObjectStatistics in keys) {
|
||
|
MonitorNetworkObject(fusionNetworkObjectStatistics.NetworkObject, fusionNetworkObjectStatistics, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_objectStatsGraphCombines?.Clear();
|
||
|
_statsGraph.Clear();
|
||
|
|
||
|
Destroy(_statsPanelObject);
|
||
|
_statsPanelObject = null;
|
||
|
|
||
|
if (Runner) {
|
||
|
if (Runner.TryGetFusionStatistics(out var statisticsManager)) {
|
||
|
statisticsManager.ObjectStatisticsManager.ClearMonitoredNetworkObjects();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool MonitorNetworkObject(NetworkObject networkObject, FusionNetworkObjectStatistics objectStatisticsInstance, bool monitor) {
|
||
|
|
||
|
if (Runner.TryGetFusionStatistics(out var statisticsManager)) {
|
||
|
statisticsManager.ObjectStatisticsManager.MonitorNetworkObjectStatistics(networkObject.Id, monitor);
|
||
|
}
|
||
|
|
||
|
if (monitor) {
|
||
|
|
||
|
// If Id already monitored on the stats, return false to destroy the object statistics instance.
|
||
|
if (_objectStatsGraphCombines.ContainsKey(objectStatisticsInstance))
|
||
|
return false;
|
||
|
|
||
|
var graphCombine = Instantiate(_objectGraphCombinePrefab, _header.ContentRect);
|
||
|
graphCombine.SetupNetworkObject(networkObject, this, objectStatisticsInstance);
|
||
|
_objectStatsGraphCombines.Add(objectStatisticsInstance, graphCombine);
|
||
|
} else {
|
||
|
|
||
|
if (_objectStatsGraphCombines.Remove(objectStatisticsInstance, out var graphCombine)) {
|
||
|
Destroy(graphCombine.gameObject);
|
||
|
Destroy(objectStatisticsInstance);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void UpdateAllGraphs(FusionStatisticsManager statisticsManager) {
|
||
|
var now = DateTime.Now;
|
||
|
foreach (var statsGraphBase in _statsGraph) {
|
||
|
statsGraphBase.UpdateGraph(Runner, statisticsManager, ref now);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void RegisterGraph(FusionStatsGraphBase graph) {
|
||
|
_statsGraph.Add(graph);
|
||
|
}
|
||
|
|
||
|
public void UnregisterGraph(FusionStatsGraphBase graph) {
|
||
|
_statsGraph.Remove(graph);
|
||
|
}
|
||
|
|
||
|
private void Update() {
|
||
|
// Safety exit
|
||
|
if (!Runner) return;
|
||
|
|
||
|
|
||
|
Profiler.BeginSample("Fusion Statistics Update Graph");
|
||
|
|
||
|
// Collect and update
|
||
|
if (Runner.TryGetFusionStatistics(out var statisticsManager)) {
|
||
|
UpdateAllGraphs(statisticsManager);
|
||
|
}
|
||
|
|
||
|
Profiler.EndSample();
|
||
|
}
|
||
|
}
|
||
|
}
|