chore: initial commit
This commit is contained in:
0
Assets/Scripts/UI/.gitkeep
Normal file
0
Assets/Scripts/UI/.gitkeep
Normal file
18
Assets/Scripts/UI/BaseGames.UI.asmdef
Normal file
18
Assets/Scripts/UI/BaseGames.UI.asmdef
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"precompiledReferences": [],
|
||||
"name": "BaseGames.UI",
|
||||
"defineConstraints": [],
|
||||
"noEngineReferences": false,
|
||||
"versionDefines": [],
|
||||
"rootNamespace": "BaseGames.UI",
|
||||
"references": [
|
||||
"BaseGames.Core",
|
||||
"BaseGames.Core.Events",
|
||||
"Unity.TextMeshPro"
|
||||
],
|
||||
"autoReferenced": true,
|
||||
"overrideReferences": false,
|
||||
"includePlatforms": []
|
||||
}
|
||||
7
Assets/Scripts/UI/BaseGames.UI.asmdef.meta
Normal file
7
Assets/Scripts/UI/BaseGames.UI.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 227d8c9f56b569340aed5e35153e22a6
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/UI/HUD.meta
Normal file
8
Assets/Scripts/UI/HUD.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9ae9298ea861bc47b2e8d8d746943d5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
130
Assets/Scripts/UI/HUD/HUDController.cs
Normal file
130
Assets/Scripts/UI/HUD/HUDController.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.UI.HUD
|
||||
{
|
||||
public class HUDController : MonoBehaviour
|
||||
{
|
||||
[Header("HP")]
|
||||
[SerializeField] private Transform _hpContainer;
|
||||
[SerializeField] private GameObject _hpCellPrefab;
|
||||
|
||||
[Header("Gauges")]
|
||||
[SerializeField] private Image _soulGaugeFill;
|
||||
[SerializeField] private Image _spiritGaugeFill;
|
||||
[SerializeField] private TMP_Text _geoText;
|
||||
|
||||
[Header("Spring Charges")]
|
||||
[SerializeField] private Transform _springContainer;
|
||||
[SerializeField] private GameObject _springIconPrefab;
|
||||
|
||||
[Header("Form")]
|
||||
[SerializeField] private Image[] _formIcons;
|
||||
|
||||
[Header("Interact Prompt")]
|
||||
[SerializeField] private TMP_Text _interactText;
|
||||
[SerializeField] private GameObject _interactPromptRoot;
|
||||
|
||||
[Header("Event Channels - Subscribe")]
|
||||
[SerializeField] private IntEventChannelSO _onHPChanged;
|
||||
[SerializeField] private IntEventChannelSO _onMaxHPChanged;
|
||||
[SerializeField] private IntEventChannelSO _onSoulPowerChanged;
|
||||
[SerializeField] private IntEventChannelSO _onSpiritPowerChanged;
|
||||
[SerializeField] private IntEventChannelSO _onGeoChanged;
|
||||
[SerializeField] private IntEventChannelSO _onSpringChargesChanged;
|
||||
[SerializeField] private IntEventChannelSO _onFormChanged;
|
||||
[SerializeField] private StringEventChannelSO _onShowInteractPrompt;
|
||||
[SerializeField] private VoidEventChannelSO _onHideInteractPrompt;
|
||||
|
||||
private readonly List<GameObject> _hpCells = new();
|
||||
private readonly List<GameObject> _springIcons = new();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_onHPChanged != null) _onHPChanged.OnEventRaised += UpdateHP;
|
||||
if (_onMaxHPChanged != null) _onMaxHPChanged.OnEventRaised += RebuildHPCells;
|
||||
if (_onSoulPowerChanged != null) _onSoulPowerChanged.OnEventRaised += UpdateSoul;
|
||||
if (_onSpiritPowerChanged != null) _onSpiritPowerChanged.OnEventRaised += UpdateSpirit;
|
||||
if (_onGeoChanged != null) _onGeoChanged.OnEventRaised += UpdateGeo;
|
||||
if (_onSpringChargesChanged != null) _onSpringChargesChanged.OnEventRaised += RebuildSpringIcons;
|
||||
if (_onFormChanged != null) _onFormChanged.OnEventRaised += UpdateFormIcon;
|
||||
if (_onShowInteractPrompt != null) _onShowInteractPrompt.OnEventRaised += ShowInteractPrompt;
|
||||
if (_onHideInteractPrompt != null) _onHideInteractPrompt.OnEventRaised += HideInteractPrompt;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_onHPChanged != null) _onHPChanged.OnEventRaised -= UpdateHP;
|
||||
if (_onMaxHPChanged != null) _onMaxHPChanged.OnEventRaised -= RebuildHPCells;
|
||||
if (_onSoulPowerChanged != null) _onSoulPowerChanged.OnEventRaised -= UpdateSoul;
|
||||
if (_onSpiritPowerChanged != null) _onSpiritPowerChanged.OnEventRaised -= UpdateSpirit;
|
||||
if (_onGeoChanged != null) _onGeoChanged.OnEventRaised -= UpdateGeo;
|
||||
if (_onSpringChargesChanged != null) _onSpringChargesChanged.OnEventRaised -= RebuildSpringIcons;
|
||||
if (_onFormChanged != null) _onFormChanged.OnEventRaised -= UpdateFormIcon;
|
||||
if (_onShowInteractPrompt != null) _onShowInteractPrompt.OnEventRaised -= ShowInteractPrompt;
|
||||
if (_onHideInteractPrompt != null) _onHideInteractPrompt.OnEventRaised -= HideInteractPrompt;
|
||||
}
|
||||
|
||||
private void UpdateHP(int current)
|
||||
{
|
||||
for (int i = 0; i < _hpCells.Count; i++)
|
||||
if (_hpCells[i] != null) _hpCells[i].SetActive(i < current);
|
||||
}
|
||||
|
||||
private void RebuildHPCells(int max)
|
||||
{
|
||||
foreach (var cell in _hpCells)
|
||||
if (cell != null) Destroy(cell);
|
||||
_hpCells.Clear();
|
||||
if (_hpContainer == null || _hpCellPrefab == null) return;
|
||||
for (int i = 0; i < max; i++)
|
||||
_hpCells.Add(Instantiate(_hpCellPrefab, _hpContainer));
|
||||
}
|
||||
|
||||
private void UpdateSoul(int val)
|
||||
{
|
||||
if (_soulGaugeFill != null) _soulGaugeFill.fillAmount = val / 100f;
|
||||
}
|
||||
|
||||
private void UpdateSpirit(int val)
|
||||
{
|
||||
if (_spiritGaugeFill != null) _spiritGaugeFill.fillAmount = val / 100f;
|
||||
}
|
||||
|
||||
private void UpdateGeo(int val)
|
||||
{
|
||||
if (_geoText != null) _geoText.text = val.ToString();
|
||||
}
|
||||
|
||||
private void RebuildSpringIcons(int charges)
|
||||
{
|
||||
foreach (var icon in _springIcons)
|
||||
if (icon != null) Destroy(icon);
|
||||
_springIcons.Clear();
|
||||
if (_springContainer == null || _springIconPrefab == null) return;
|
||||
for (int i = 0; i < charges; i++)
|
||||
_springIcons.Add(Instantiate(_springIconPrefab, _springContainer));
|
||||
}
|
||||
|
||||
private void UpdateFormIcon(int formIndex)
|
||||
{
|
||||
if (_formIcons == null) return;
|
||||
for (int i = 0; i < _formIcons.Length; i++)
|
||||
if (_formIcons[i] != null) _formIcons[i].enabled = (i == formIndex);
|
||||
}
|
||||
|
||||
private void ShowInteractPrompt(string text)
|
||||
{
|
||||
if (_interactText != null) _interactText.text = text;
|
||||
if (_interactPromptRoot != null) _interactPromptRoot.SetActive(true);
|
||||
}
|
||||
|
||||
private void HideInteractPrompt()
|
||||
{
|
||||
if (_interactPromptRoot != null) _interactPromptRoot.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/HUD/HUDController.cs.meta
Normal file
11
Assets/Scripts/UI/HUD/HUDController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 533b885673d509d419e441a7264261a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/UI/Menus.meta
Normal file
8
Assets/Scripts/UI/Menus.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cce9781f030e49648ba1939ca3cfdaa5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
52
Assets/Scripts/UI/Menus/DeathScreenController.cs
Normal file
52
Assets/Scripts/UI/Menus/DeathScreenController.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.UI.Menus
|
||||
{
|
||||
public class DeathScreenController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_Text _deathMessage;
|
||||
[SerializeField] private Button _btnRespawn;
|
||||
|
||||
[Header("Event Channels")]
|
||||
[SerializeField] private VoidEventChannelSO _onPlayerDied;
|
||||
[SerializeField] private VoidEventChannelSO _onDeathScreenConfirmed;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_onPlayerDied != null) _onPlayerDied.OnEventRaised += OnPlayerDied;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_onPlayerDied != null) _onPlayerDied.OnEventRaised -= OnPlayerDied;
|
||||
}
|
||||
|
||||
private void OnPlayerDied() => StartCoroutine(ShowAfterDelay(1.5f));
|
||||
|
||||
private IEnumerator ShowAfterDelay(float delay)
|
||||
{
|
||||
yield return new WaitForSeconds(delay);
|
||||
Show();
|
||||
}
|
||||
|
||||
private void Show()
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
if (_btnRespawn != null)
|
||||
{
|
||||
_btnRespawn.onClick.RemoveAllListeners();
|
||||
_btnRespawn.onClick.AddListener(Confirm);
|
||||
}
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
_onDeathScreenConfirmed?.Raise();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/Menus/DeathScreenController.cs.meta
Normal file
11
Assets/Scripts/UI/Menus/DeathScreenController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6773e585eac299448529521e4b090c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
81
Assets/Scripts/UI/UIManager.cs
Normal file
81
Assets/Scripts/UI/UIManager.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.UI
|
||||
{
|
||||
[DefaultExecutionOrder(+50)]
|
||||
public class UIManager : MonoBehaviour
|
||||
{
|
||||
[Header("Canvas Roots")]
|
||||
[SerializeField] private GameObject _hudRoot;
|
||||
[SerializeField] private GameObject _pauseMenuRoot;
|
||||
[SerializeField] private GameObject _deathScreenRoot;
|
||||
[SerializeField] private GameObject _settingsRoot;
|
||||
[SerializeField] private GameObject _mapRoot;
|
||||
[SerializeField] private GameObject _shopRoot;
|
||||
|
||||
[Header("Event Channels")]
|
||||
[SerializeField] private GameStateEventChannelSO _onGameStateChanged;
|
||||
[SerializeField] private VoidEventChannelSO _onPauseRequested;
|
||||
[SerializeField] private VoidEventChannelSO _onFastTravelOpen;
|
||||
[SerializeField] private StringEventChannelSO _onShopOpen;
|
||||
[SerializeField] private VoidEventChannelSO _onMapOpen;
|
||||
|
||||
private readonly Stack<GameObject> _panelStack = new();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_onGameStateChanged != null) _onGameStateChanged.OnEventRaised += HandleGameStateChanged;
|
||||
if (_onPauseRequested != null) _onPauseRequested.OnEventRaised += TogglePause;
|
||||
if (_onFastTravelOpen != null) _onFastTravelOpen.OnEventRaised += OpenMap;
|
||||
if (_onShopOpen != null) _onShopOpen.OnEventRaised += OpenShop;
|
||||
if (_onMapOpen != null) _onMapOpen.OnEventRaised += OpenMap;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_onGameStateChanged != null) _onGameStateChanged.OnEventRaised -= HandleGameStateChanged;
|
||||
if (_onPauseRequested != null) _onPauseRequested.OnEventRaised -= TogglePause;
|
||||
if (_onFastTravelOpen != null) _onFastTravelOpen.OnEventRaised -= OpenMap;
|
||||
if (_onShopOpen != null) _onShopOpen.OnEventRaised -= OpenShop;
|
||||
if (_onMapOpen != null) _onMapOpen.OnEventRaised -= OpenMap;
|
||||
}
|
||||
|
||||
private void HandleGameStateChanged(GameStateId state)
|
||||
{
|
||||
// GameStateId 是 struct,用 if/else 而非 switch
|
||||
bool showHud = state == GameStates.Gameplay || state == GameStates.BossFight;
|
||||
if (_hudRoot != null) _hudRoot.SetActive(showHud);
|
||||
|
||||
if (state == GameStates.Dead)
|
||||
{
|
||||
if (_deathScreenRoot != null) _deathScreenRoot.SetActive(true);
|
||||
}
|
||||
else if (state == GameStates.Cutscene)
|
||||
{
|
||||
if (_hudRoot != null) _hudRoot.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenPanel(GameObject panel)
|
||||
{
|
||||
if (panel == null) return;
|
||||
if (_panelStack.Count > 0) _panelStack.Peek().SetActive(false);
|
||||
panel.SetActive(true);
|
||||
_panelStack.Push(panel);
|
||||
}
|
||||
|
||||
public void CloseTopPanel()
|
||||
{
|
||||
if (_panelStack.Count == 0) return;
|
||||
_panelStack.Pop().SetActive(false);
|
||||
if (_panelStack.Count > 0) _panelStack.Peek().SetActive(true);
|
||||
}
|
||||
|
||||
private void TogglePause() => OpenPanel(_pauseMenuRoot);
|
||||
private void OpenShop(string _) => OpenPanel(_shopRoot);
|
||||
private void OpenMap() => OpenPanel(_mapRoot);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/UIManager.cs.meta
Normal file
11
Assets/Scripts/UI/UIManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b9dc4d5fa79326428150ef020e32fe4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/Scripts/UI/_Placeholder.cs
Normal file
3
Assets/Scripts/UI/_Placeholder.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
// Placeholder to prevent asmdef-no-scripts warning.
|
||||
namespace BaseGames.UI { }
|
||||
|
||||
11
Assets/Scripts/UI/_Placeholder.cs.meta
Normal file
11
Assets/Scripts/UI/_Placeholder.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2087fe261337fef4d9be0845a93bc890
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user