完整启动流程
This commit is contained in:
138
Assets/_Game/Scripts/UI/Splash/SplashScreenController.cs
Normal file
138
Assets/_Game/Scripts/UI/Splash/SplashScreenController.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user