139 lines
5.8 KiB
C#
139 lines
5.8 KiB
C#
using System.Collections;
|
||
using UnityEngine;
|
||
using BaseGames.Core.Events;
|
||
|
||
namespace BaseGames.UI.Splash
|
||
{
|
||
/// <summary>
|
||
/// 游戏启动 Logo 演出控制器。挂载在 Persistent 场景的 Canvas_Splash 根节点。
|
||
///
|
||
/// 演出顺序:工作室 Logo 淡入 → 停留 → 淡出 → 游戏标题淡入 → 停留 → 黑幕整体淡出。
|
||
/// 任意按键 / 手柄键可跳过整段演出。
|
||
///
|
||
/// 与 Core 程序集完全解耦:
|
||
/// 监听 EVT_SplashStartRequest(VoidEventChannelSO)→ 开始演出。
|
||
/// 完成后发布 EVT_SplashComplete(VoidEventChannelSO)→ 通知 BootSequencer 继续。
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
}
|