Files
aibis-dream/Assets/Scripts/UI/Terminal/TerminalPanel.cs
T
dingyuntian e015b756f6 feat(audio-kit): 终端 UI 菜单音效与暂停菜单环境音
- 新增 TerminalUIAudio 事件通知与 TerminalUIAudioBehaviour 组件
- TerminalButton/TerminalMenuItemView/SelectItem/SliderItem 接入悬停与点击音效
- AudioManager 监听 TerminalUIEvent 播放 menu_hover/menu_click
- 新增 PauseMenu 环境音状态及恢复逻辑
- 新增 MainMenuStage FMOD 参数控制
- BaseButton 调用基类 DoStateTransition 保证状态事件正确触发
- 为 TerminalChapterPanel Prefab 添加 TerminalUIAudioBehaviour
2026-07-04 23:12:47 +08:00

338 lines
9.1 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;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
namespace AibisDream.UI
{
public enum TerminalPage
{
None,
Start,
Setting,
Chapter,
}
enum TerminalBootPhase
{
None,
PressAnyKey,
PlayingIntro,
PostIntroPause,
}
/// <summary>
/// 主菜单终端 Panel 壳:管理 Start / Chapter / Setting 三个 Page。
/// 冷启动时先走 Intro TimelinePress Any Key → 播放 → 停顿 → Start);同 session 内回主菜单跳过 Intro。
/// </summary>
public class TerminalPanel : MonoBehaviour, IUIPanel, ITerminalPanelHost
{
[SerializeField] private TerminalHeaderView header;
[SerializeField] private TerminalStartPanel startPage;
[SerializeField] private TerminalSettingPanel settingPage;
[SerializeField] private TerminalChapterPanel chapterPage;
[SerializeField] private PlayableDirector introDirector;
[SerializeField] private float postIntroPause = 0.5f;
[SerializeField] private GameObject menuFrame;
private readonly Stack<TerminalPage> _backStack = new();
private TerminalPage _currentPage = TerminalPage.None;
private bool _introCompletedThisSession;
private TerminalBootPhase _bootPhase = TerminalBootPhase.None;
public bool IsOpen =>
gameObject.activeSelf &&
(_bootPhase != TerminalBootPhase.None || _currentPage != TerminalPage.None);
public bool IsCloseable => false;
public TerminalPage CurrentPage => _currentPage;
public bool IsInGameContext => false;
public void Initialize()
{
startPage?.SetOwner(this);
settingPage?.SetOwner(this);
chapterPage?.SetOwner(this);
if (!HasRequiredPages())
{
Debug.LogError("[TerminalPanel] Missing required pages.");
}
Close();
}
public bool HasRequiredPages()
{
return startPage != null && settingPage != null && chapterPage != null;
}
public void Show()
{
if (_introCompletedThisSession)
{
TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Interactive);
Open(TerminalPage.Start);
return;
}
if (introDirector == null)
{
Debug.LogError("[TerminalPanel] introDirector is not assigned. Opening Start directly.");
_introCompletedThisSession = true;
TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Interactive);
Open(TerminalPage.Start);
return;
}
BeginBootSequence();
}
public void Hide()
{
Close();
}
private void Update()
{
if (_bootPhase != TerminalBootPhase.PressAnyKey)
{
return;
}
if (!TryGetBootInput())
{
return;
}
TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Interactive);
StartCoroutine(PlayIntroAndRevealStart());
}
private static bool TryGetBootInput()
{
if (Input.GetMouseButtonDown(0))
{
return true;
}
return Input.anyKeyDown;
}
private void BeginBootSequence()
{
gameObject.SetActive(true);
_backStack.Clear();
_currentPage = TerminalPage.None;
HideAllPages();
_bootPhase = TerminalBootPhase.PressAnyKey;
TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Boot);
}
private IEnumerator PlayIntroAndRevealStart()
{
_bootPhase = TerminalBootPhase.PlayingIntro;
introDirector.Play();
while (introDirector.state == PlayState.Playing)
{
yield return null;
}
_bootPhase = TerminalBootPhase.PostIntroPause;
if (postIntroPause > 0f)
{
yield return new WaitForSecondsRealtime(postIntroPause);
}
_introCompletedThisSession = true;
_bootPhase = TerminalBootPhase.None;
menuFrame.SetActive(true);
Open(TerminalPage.Start);
}
public void Open(TerminalPage page)
{
if (!HasRequiredPages() || page == TerminalPage.None)
{
return;
}
if (!IsOpen)
{
_backStack.Clear();
}
gameObject.SetActive(true);
SwitchTo(page, pushCurrent: false);
}
public void SwitchTo(TerminalPage page, bool pushCurrent = true)
{
if (page == TerminalPage.None || page == _currentPage)
{
return;
}
if (pushCurrent && _currentPage != TerminalPage.None)
{
_backStack.Push(_currentPage);
}
var previous = _currentPage;
_currentPage = page;
if (previous == TerminalPage.None)
{
ShowPage(_currentPage);
return;
}
HidePage(previous, () => ShowPage(_currentPage));
}
public void Back()
{
if (_backStack.Count > 0)
{
var previous = _backStack.Pop();
var current = _currentPage;
_currentPage = previous;
HidePage(current, () => ShowPage(_currentPage));
return;
}
if (_currentPage != TerminalPage.Start)
{
SwitchTo(TerminalPage.Start, pushCurrent: false);
}
}
public bool HandleEscape()
{
if (!gameObject.activeSelf)
{
return false;
}
if (_bootPhase != TerminalBootPhase.None)
{
return true;
}
if (_currentPage == TerminalPage.None)
{
return false;
}
if (_currentPage == TerminalPage.Start)
{
return true;
}
Back();
return true;
}
public void CloseThen(Action action)
{
Close();
action?.Invoke();
}
public void Close()
{
if (introDirector != null)
{
introDirector.Stop();
}
HideAllPages();
_backStack.Clear();
_currentPage = TerminalPage.None;
_bootPhase = TerminalBootPhase.None;
gameObject.SetActive(false);
}
public void CloseToMainMenu()
{
Close();
GameManager.Instance.QuitGame();
}
public void SetHeader(string title, string status = "")
{
header?.SetText(title, status);
}
private void ShowPage(TerminalPage page)
{
switch (page)
{
case TerminalPage.Start:
startPage?.Show();
break;
case TerminalPage.Setting:
settingPage?.Show();
break;
case TerminalPage.Chapter:
chapterPage?.Show();
break;
}
}
private void HidePage(TerminalPage page, Action onComplete = null)
{
switch (page)
{
case TerminalPage.Start:
HidePage(startPage, onComplete);
break;
case TerminalPage.Setting:
HidePage(settingPage, onComplete);
break;
case TerminalPage.Chapter:
HidePage(chapterPage, onComplete);
break;
default:
onComplete?.Invoke();
break;
}
}
private static void HidePage(MonoBehaviour page, Action onComplete)
{
if (page == null)
{
onComplete?.Invoke();
return;
}
switch (page)
{
case TerminalStartPanel startPanel:
startPanel.Hide(onComplete);
break;
case TerminalSettingPanel settingPanel:
settingPanel.Hide(onComplete);
break;
case TerminalChapterPanel chapterPanel:
chapterPanel.Hide(onComplete);
break;
default:
page.gameObject.SetActive(false);
onComplete?.Invoke();
break;
}
}
private void HideAllPages()
{
startPage?.Hide();
settingPage?.Hide();
chapterPage?.Hide();
}
}
}