using System.Collections; using SepCore.Event; using GameFramework.Event; using SepCore.EnemyManager; using UnityEngine; using Unity.Profiling; namespace SepCore.UI { public partial class HudForm : UGuiForm { [SerializeField] private float _sliderFadeDuration = 0.5f; private float _currentHpPercent = 0; private Coroutine _hpSliderFadeCoroutine; private float _currentExpPercent = 0; private Coroutine _expSliderFadeCoroutine; private int _playerCurrentLevel = 1; private int _currentCoin = 0; private int _currentTimeLeft = 0; private EnemyManagerComponent _enemy; [SerializeField] private float _performanceStatsRefreshInterval = 0.2f; private ProfilerRecorder _cpuTimeRecorder; private float _performanceStatsRefreshTimer; private Coroutine _performanceStatsCoroutine; public void RefreshUI(HudContext hudContext) { RefreshMovePrompt(hudContext.MovePrompt); } public void RefreshMovePrompt(string prompt) { bool visible = !string.IsNullOrEmpty(prompt); View.inputPromptObject.gameObject.SetActive(visible); View.inputPromptText.text = prompt; } #region FSM protected override void OnInit(object userData) { base.OnInit(userData); GameEntry.Event.Subscribe(PlayerHealthChangeEventArgs.EventId, PlayerHpChange); GameEntry.Event.Subscribe(PlayerExpChangeEventArgs.EventId, PlayerExpChange); GameEntry.Event.Subscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange); GameEntry.Event.Subscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp); GameEntry.Event.Subscribe(LevelProcessEventArgs.EventId, LevelProcess); _enemy = GameEntry.EnemyManager; } protected override void OnOpen(object userData) { base.OnOpen(userData); StartPerformanceStatsCollection(); } protected override void OnPause() { StopPerformanceStatsCoroutine(); base.OnPause(); } protected override void OnResume() { base.OnResume(); StartPerformanceStatsCoroutine(); } private void StartPerformanceStatsCollection() { DisposePerformanceStatsRecorders(); _cpuTimeRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Internal, "Main Thread", 15); StartPerformanceStatsCoroutine(); } private void StartPerformanceStatsCoroutine() { StopPerformanceStatsCoroutine(); _performanceStatsRefreshTimer = 0f; _performanceStatsCoroutine = StartCoroutine(RefreshPerformanceStatsAtEndOfFrame()); } protected override void OnClose(bool isShutdown, object userData) { GameEntry.Event.Unsubscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp); GameEntry.Event.Unsubscribe(PlayerHealthChangeEventArgs.EventId, PlayerHpChange); GameEntry.Event.Unsubscribe(PlayerExpChangeEventArgs.EventId, PlayerExpChange); GameEntry.Event.Unsubscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange); GameEntry.Event.Unsubscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp); StopPerformanceStatsCoroutine(); DisposePerformanceStatsRecorders(); base.OnClose(isShutdown, userData); } private void StopPerformanceStatsCoroutine() { if (_performanceStatsCoroutine != null) { StopCoroutine(_performanceStatsCoroutine); _performanceStatsCoroutine = null; } } private void DisposePerformanceStatsRecorders() { if (_cpuTimeRecorder.Valid) { _cpuTimeRecorder.Dispose(); } } protected override void OnUpdate(float elapseSeconds, float realElapseSeconds) { base.OnUpdate(elapseSeconds, realElapseSeconds); View.enemyCountFormatText.Set(_enemy.CurrentEnemyCount); } #endregion private IEnumerator RefreshPerformanceStatsAtEndOfFrame() { WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame(); while (true) { yield return waitForEndOfFrame; _performanceStatsRefreshTimer -= Time.unscaledDeltaTime; if (_performanceStatsRefreshTimer > 0f) continue; _performanceStatsRefreshTimer = Mathf.Max(0.05f, _performanceStatsRefreshInterval); RefreshPerformanceStats(); } } private void RefreshPerformanceStats() { bool hasCpuFrame = TryGetLastValue(_cpuTimeRecorder, out long cpuFrameNanoseconds); if (!hasCpuFrame) { View.cpuTimeCostFormatText.Set(-1); return; } float cpuFrameMilliseconds = cpuFrameNanoseconds / 1_000_000f; View.cpuTimeCostFormatText.Set(cpuFrameMilliseconds); } private static bool TryGetLastValue(ProfilerRecorder recorder, out long value) { if (recorder.Valid && recorder.Count > 0) { value = recorder.LastValue; return true; } value = 0; return false; } #region Event Handlers private void PlayerHpChange(object sender, GameEventArgs e) { if (!(e is PlayerHealthChangeEventArgs args)) return; View.hpFormatText.Set(args.CurrentHealth, args.MaxHealth); float percent = (float)args.CurrentHealth / args.MaxHealth; if (Mathf.Approximately(_currentHpPercent, percent)) return; _currentHpPercent = percent; if (_hpSliderFadeCoroutine != null) StopCoroutine(_hpSliderFadeCoroutine); _hpSliderFadeCoroutine = StartCoroutine(View.hpSlider.SmoothValue(percent, _sliderFadeDuration)); } private void PlayerExpChange(object sender, GameEventArgs e) { if (!(e is PlayerExpChangeEventArgs args)) return; float percent = (float)args.CurrentExp / args.MaxExp; if (Mathf.Approximately(_currentExpPercent, percent)) return; _currentExpPercent = percent; if (_expSliderFadeCoroutine != null) StopCoroutine(_expSliderFadeCoroutine); _expSliderFadeCoroutine = StartCoroutine(View.expSlider.SmoothValue(percent, _sliderFadeDuration)); } private void PlayerLevelUp(object sender, GameEventArgs e) { if (!(e is PlayerLevelUpEventArgs)) return; _playerCurrentLevel++; View.expFormatText.Set(_playerCurrentLevel); } private void PlayerCoinChange(object sender, GameEventArgs e) { if (!(e is PlayerCoinChangeEventArgs args)) return; if (_currentCoin == args.CoinCount) return; View.coinFormatText.Set(args.CoinCount); _currentCoin = args.CoinCount; } private void LevelProcess(object sender, GameEventArgs e) { if (!(e is LevelProcessEventArgs args)) return; if (_currentTimeLeft == args.LevelTimeLeft) return; _currentTimeLeft = args.LevelTimeLeft; View.countdownText.text = _currentTimeLeft.ToString(); } #endregion } }