using System.Collections; using UnityEngine; using BaseGames.Core.Events; namespace BaseGames.UI.Splash { /// /// 游戏启动 Logo 演出控制器。挂载在 Persistent 场景的 Canvas_Splash 根节点。 /// /// 演出顺序:工作室 Logo 淡入 → 停留 → 淡出 → 游戏标题淡入 → 停留 → 黑幕整体淡出。 /// 任意按键 / 手柄键可跳过整段演出。 /// /// 与 Core 程序集完全解耦: /// 监听 EVT_SplashStartRequest(VoidEventChannelSO)→ 开始演出。 /// 完成后发布 EVT_SplashComplete(VoidEventChannelSO)→ 通知 BootSequencer 继续。 /// public class SplashScreenController : MonoBehaviour { [Header("Canvas Groups")] [Tooltip("整个 Splash 画布的根节点 CanvasGroup(用于整体淡出黑幕)")] [SerializeField] private CanvasGroup _splashRoot; [Tooltip("工作室 Logo CanvasGroup")] [SerializeField] private CanvasGroup _studioLogoGroup; [Tooltip("游戏标题 Logo CanvasGroup")] [SerializeField] private CanvasGroup _gameTitleGroup; [Header("时序(秒)")] [SerializeField] private float _fadeInDuration = 0.8f; [SerializeField] private float _holdDuration = 1.5f; [SerializeField] private float _fadeOutDuration = 0.6f; [SerializeField] private float _stageGapDuration = 0.3f; [Header("Event Channels")] [SerializeField] private VoidEventChannelSO _onSplashStartRequest; // Listen [SerializeField] private VoidEventChannelSO _onSplashComplete; // Raise private bool _skipRequested; private readonly CompositeDisposable _subs = new(); // ── 生命周期 ───────────────────────────────────────────────────────── private void Awake() { // 初始态:根节点完全不透明(充当开场黑幕),各 Logo 完全透明 SetAlpha(_splashRoot, 1f, blocksRaycasts: true); SetAlpha(_studioLogoGroup, 0f, blocksRaycasts: false); SetAlpha(_gameTitleGroup, 0f, blocksRaycasts: false); } private void OnEnable() => _onSplashStartRequest?.Subscribe(OnStartRequested).AddTo(_subs); private void OnDisable() => _subs.Clear(); // ── 入口 ───────────────────────────────────────────────────────────── private void OnStartRequested() => StartCoroutine(PlayAndNotify()); private IEnumerator PlayAndNotify() { yield return StartCoroutine(PlayAsync()); _onSplashComplete?.Raise(); } // ── 演出主逻辑 ─────────────────────────────────────────────────────── private IEnumerator PlayAsync() { _skipRequested = false; // Stage 1:工作室 Logo if (_studioLogoGroup != null) { yield return StartCoroutine(FadeGroup(_studioLogoGroup, 0f, 1f, _fadeInDuration)); yield return StartCoroutine(WaitOrSkip(_holdDuration)); yield return StartCoroutine(FadeGroup(_studioLogoGroup, 1f, 0f, _fadeOutDuration)); } if (!_skipRequested) yield return new WaitForSecondsRealtime(_stageGapDuration); // Stage 2:游戏标题 if (_gameTitleGroup != null) { yield return StartCoroutine(FadeGroup(_gameTitleGroup, 0f, 1f, _fadeInDuration)); yield return StartCoroutine(WaitOrSkip(_holdDuration)); yield return StartCoroutine(FadeGroup(_gameTitleGroup, 1f, 0f, _fadeOutDuration)); } // 黑幕整体淡出(显露主菜单场景) yield return StartCoroutine(FadeGroup(_splashRoot, 1f, 0f, _fadeOutDuration)); if (_splashRoot != null) _splashRoot.blocksRaycasts = false; gameObject.SetActive(false); } // ── Unity 输入(任意按键跳过)──────────────────────────────────────── private void Update() { if (Input.anyKeyDown) _skipRequested = true; } // ── 内部工具 ───────────────────────────────────────────────────────── private IEnumerator FadeGroup(CanvasGroup group, float from, float to, float duration) { if (group == null) yield break; group.alpha = from; float elapsed = 0f; while (elapsed < duration && !_skipRequested) { group.alpha = Mathf.Lerp(from, to, elapsed / duration); elapsed += Time.unscaledDeltaTime; yield return null; } group.alpha = to; } private IEnumerator WaitOrSkip(float duration) { float elapsed = 0f; while (elapsed < duration && !_skipRequested) { elapsed += Time.unscaledDeltaTime; yield return null; } } private static void SetAlpha(CanvasGroup group, float alpha, bool blocksRaycasts) { if (group == null) return; group.alpha = alpha; group.blocksRaycasts = blocksRaycasts; } } }