chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

View 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": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 227d8c9f56b569340aed5e35153e22a6
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c9ae9298ea861bc47b2e8d8d746943d5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 533b885673d509d419e441a7264261a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cce9781f030e49648ba1939ca3cfdaa5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6773e585eac299448529521e4b090c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b9dc4d5fa79326428150ef020e32fe4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
// Placeholder to prevent asmdef-no-scripts warning.
namespace BaseGames.UI { }

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2087fe261337fef4d9be0845a93bc890
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: