迁移剩下的 UI 到 Serialization 方式
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class HudContext : UIContext
|
||||
{
|
||||
public string MovePrompt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f8d476720436b84483bd1c29da4d558
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,67 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SepCore.Definition;
|
||||
using SepCore.InputModule;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class HudController : UIControllerBase<HudContext, HudForm>
|
||||
{
|
||||
protected override UIFormType UIFormType => UIFormType.HudForm;
|
||||
|
||||
protected override void RefreshUI(HudForm form, HudContext context)
|
||||
{
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.InputModule.DeviceKindChanged += OnDeviceKindChanged;
|
||||
}
|
||||
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.InputModule.DeviceKindChanged -= OnDeviceKindChanged;
|
||||
}
|
||||
|
||||
private static HudContext BuildHudContext()
|
||||
{
|
||||
return new HudContext
|
||||
{
|
||||
MovePrompt = BuildPromptContext(InputActionId.Move)
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildPromptContext(InputActionId actionId)
|
||||
{
|
||||
if (!GameEntry.InputModule.TryGetPrompt(actionId, out InputPrompt prompt))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return InputPromptTextUtility.BuildTmpText(prompt);
|
||||
}
|
||||
|
||||
public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f)
|
||||
{
|
||||
if (userData != null)
|
||||
{
|
||||
Log.Error("HudController.OpenUIAsync() userData type is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
await OpenFormAsync(BuildHudContext(), timeout);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
Log.Info("HudFormController doesn't need UseCase");
|
||||
}
|
||||
|
||||
private void OnDeviceKindChanged(InputDeviceKind deviceKind)
|
||||
{
|
||||
Context.MovePrompt = BuildPromptContext(InputActionId.Move);
|
||||
Form.RefreshMovePrompt(Context.MovePrompt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7360b01093fa984d86105e2dd024dd5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,234 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd4c4d6e8d4b0f54c830e2cfef46848e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
// <auto-generated />
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public partial class HudForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private HudView _view = null;
|
||||
|
||||
public HudView View => _view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f50fc6aab9e9b4846b0a80dd670c94b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
// <auto-generated />
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public partial class HudView : MonoBehaviour
|
||||
{
|
||||
[SerializeField] public global::UnityEngine.UI.Slider hpSlider;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI hpFormatText;
|
||||
[SerializeField] public global::UnityEngine.UI.Slider expSlider;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI expFormatText;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI coinFormatText;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI enemyCountFormatText;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI cpuTimeCostFormatText;
|
||||
[SerializeField] public global::TMPro.TextMeshProUGUI countdownText;
|
||||
[SerializeField] public global::UnityEngine.RectTransform inputPromptObject;
|
||||
[SerializeField] public global::TMPro.TextMeshProUGUI inputPromptText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 128e0c828a462a242a89114f9c8b9d32
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user