多轮审查和修复

This commit is contained in:
2026-05-12 15:34:08 +08:00
parent f55d2a57c3
commit ebbbb7332e
805 changed files with 838724 additions and 1905 deletions

View File

@@ -0,0 +1,126 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.Core.Events;
namespace BaseGames.UI
{
/// <summary>
/// 单条通知弹窗(架构 10_UIModule §11
/// 由 ToastManager 控制显示和自动隐藏。
/// </summary>
[RequireComponent(typeof(CanvasGroup))]
public class ToastNotification : MonoBehaviour
{
[SerializeField] private TMP_Text _titleText;
[SerializeField] private TMP_Text _bodyText;
[SerializeField] private Image _icon;
[SerializeField] private float _displayDuration = 3f;
[SerializeField] private float _fadeDuration = 0.25f;
private CanvasGroup _cg;
private Coroutine _hideCoroutine;
/// <summary>淡入 + 保持 + 淡出的总时长(供 ToastManager 队列计时用)。</summary>
public float TotalTime => _displayDuration + _fadeDuration * 2f;
private void Awake()
{
_cg = GetComponent<CanvasGroup>();
_cg.alpha = 0f;
}
public void Show(string title, string body, Sprite icon = null)
{
if (_titleText != null) _titleText.text = title;
if (_bodyText != null) _bodyText.text = body;
if (_icon != null)
{
_icon.sprite = icon;
_icon.enabled = icon != null;
}
gameObject.SetActive(true);
if (_hideCoroutine != null) StopCoroutine(_hideCoroutine);
_hideCoroutine = StartCoroutine(AutoHide());
}
private IEnumerator AutoHide()
{
// 淡入
yield return StartCoroutine(FadeTo(1f));
// 保持
yield return new WaitForSecondsRealtime(_displayDuration);
// 淡出
yield return StartCoroutine(FadeTo(0f));
gameObject.SetActive(false);
}
private IEnumerator FadeTo(float target)
{
float start = _cg.alpha;
float elapsed = 0f;
while (elapsed < _fadeDuration)
{
_cg.alpha = Mathf.Lerp(start, target, elapsed / _fadeDuration);
elapsed += Time.unscaledDeltaTime;
yield return null;
}
_cg.alpha = target;
}
}
// ─────────────────────────────────────────────────────────────────────────
/// <summary>
/// 通知队列管理器(架构 10_UIModule §11 ToastManager
/// 同时只显示一条 Toast队列不为空时前一条结束后立即显示下一条。
/// 订阅 EVT_AchievementUnlocked / EVT_AbilityUnlocked 事件频道。
/// </summary>
public class ToastManager : MonoBehaviour
{
[SerializeField] private ToastNotification _toast;
[Header("Event Channels")]
[SerializeField] private StringEventChannelSO _onAchievementUnlocked; // EVT_AchievementUnlocked
[SerializeField] private StringEventChannelSO _onAbilityUnlocked; // EVT_AbilityUnlocked
private readonly Queue<(string title, string body, Sprite icon)> _queue = new();
private readonly CompositeDisposable _subs = new();
private bool _showing;
private void OnEnable()
{
_onAchievementUnlocked?.Subscribe(OnAchievement).AddTo(_subs);
_onAbilityUnlocked?.Subscribe(OnAbility).AddTo(_subs);
}
private void OnDisable()
{
_subs.Clear();
}
private void OnAchievement(string id) => Enqueue("成就解锁", id, null);
private void OnAbility(string abilityId) => Enqueue("能力获得", abilityId, null);
public void Enqueue(string title, string body, Sprite icon = null)
{
_queue.Enqueue((title, body, icon));
if (!_showing) StartCoroutine(ProcessQueue());
}
private IEnumerator ProcessQueue()
{
_showing = true;
while (_queue.Count > 0)
{
var (title, body, icon) = _queue.Dequeue();
if (_toast != null) _toast.Show(title, body, icon);
// 等待 Toast 完成后再显示下一条(与 ToastNotification._displayDuration/_fadeDuration 保持同步)
float wait = _toast != null ? _toast.TotalTime + 0.1f : 3.6f;
yield return new WaitForSecondsRealtime(wait);
}
_showing = false;
}
}
}