Files
zeling_v2/Assets/Scripts/UI/ToastManager.cs
2026-05-12 15:34:08 +08:00

127 lines
4.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}