feat(ui): 添加 TerminalPanel 统一终端 UI 导航并移除旧面板

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-01 19:03:14 +08:00
co-authored by Cursor
parent 4deb565882
commit 024202b19e
20 changed files with 472 additions and 685 deletions
@@ -260,7 +260,7 @@ namespace AibisDream.SaveSystem
/// <summary>
/// 读档前对齐「已进入游戏」的 UI 与输入状态。
/// 编辑器测试工具等路径可能跳过 <see cref="GameLoopEnum.GameStart"/>,导致 StartPanel 未关闭、
/// 编辑器测试工具等路径可能跳过 <see cref="GameLoopEnum.GameStart"/>,导致 TerminalPanel 未关闭、
/// <see cref="GameManager.state.isInGame"/> 为 false,从而无法推进对话。
/// </summary>
private static void PrepareGameSessionForRestore()
@@ -276,9 +276,8 @@ namespace AibisDream.SaveSystem
var ui = UIManager.Instance;
if (ui != null)
{
ui.HidePreferredStartPanel();
ui.HideTerminal();
ui.ShowPanel<MainPanel>();
ui.HidePreferredChapterPanel();
ui.HidePanel<SavesPanel>();
ui.HidePanel<EndPanel>();
}
+2 -18
View File
@@ -29,20 +29,6 @@ namespace AibisDream
EnumEventSystem.Global.UnRegister(GameLoopEnum.GameQuit, CleanDialogHistory);
}
private RecordPanel RecordPanel => UIManager.Instance.GetPanel<RecordPanel>();
private TerminalRecordPanel TerminalRecordPanel
{
get
{
UIManager.Instance.TryGetPanel<TerminalRecordPanel>(out var panel);
return panel;
}
}
/// <summary>
/// 记录对话
/// </summary>
/// <param name="line">对话</param>
private void AddDialogueLine(LineInfo line)
{
if (line.character.dialogViewType == DialogViewType.Task)
@@ -50,14 +36,12 @@ namespace AibisDream
return;
}
RecordPanel?.AppendDialogLine(line);
TerminalRecordPanel?.AppendDialogLine(line);
UIManager.Instance.GetPanel<TerminalPanel>()?.AppendDialogLine(line);
}
private void CleanDialogHistory()
{
RecordPanel?.ClearDialog();
TerminalRecordPanel?.ClearDialog();
UIManager.Instance.GetPanel<TerminalPanel>()?.ClearDialog();
}
public void CloseCanvas()
-146
View File
@@ -1,146 +0,0 @@
using AibisDream.Kit;
using AibisDream.Utility;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.UI
{
public class ChapterPanel : MonoBehaviour, IUIPanel
{
#region
public ChapterController chapterController;
public Animator animator;
public Image background;
public RectTransform chapterContentRect;
public TMP_Text errorInfo;
public GameObject chapterDetail;
public TMP_Text chapterDetailText;
public Button continueButton;
public Button restartButton;
public GameObject chapterItemPrefab;
#endregion
private GameObject _background;
private ChapterVo _curChapterVo;
public void Show()
{
gameObject.SetActive(true);
AddBackground();
animator.Play("Open", 0, 0);
ActionKit.Delay(ConstRef.PanelDelayDuration, DrawContent).Start(this);
}
public void Hide()
{
RemoveBackground();
animator.Play("Close", 0, 0);
ActionKit.Delay(ConstRef.PanelDelayDuration, DestroyContent).Start(this);
}
private void AddBackground()
{
background.canvasRenderer.SetAlpha(0.0f);
background.gameObject.SetActive(true);
background.CrossFadeAlpha(1.0f, 0.4f, false);
}
private void RemoveBackground()
{
background.CrossFadeAlpha(0.0f, 0.2f, false);
}
#region
private void DrawContent()
{
chapterContentRect.DestroyAllChildren();
chapterDetail.SetActive(false);
continueButton.onClick.AddListener(OnContinueClick);
restartButton.onClick.AddListener(OnRestartClick);
var chapterList = chapterController.GetChapterList();
if (chapterList.Count <= 0)
{
errorInfo.gameObject.SetActive(true);
}
else
{
errorInfo.gameObject.SetActive(false);
foreach (var chapterVo in chapterList)
{
var chapterItem = Instantiate(chapterItemPrefab, chapterContentRect).GetComponent<ChapterItem>();
chapterItem.Init(chapterVo);
chapterItem.OnClick += OnChapterSelected;
}
}
}
private void DestroyContent()
{
chapterDetail.SetActive(false);
_curChapterVo = default;
continueButton.onClick.RemoveListener(OnContinueClick);
restartButton.onClick.RemoveListener(OnRestartClick);
chapterContentRect.DestroyAllChildren();
gameObject.SetActive(false);
Destroy(_background);
background.gameObject.SetActive(false);
}
private void OnChapterSelected(ChapterVo chapterVo)
{
chapterDetail.SetActive(true);
chapterDetailText.text = chapterVo.desc;
continueButton.gameObject.SetActive(chapterVo.hasSaveFile);
_curChapterVo = chapterVo;
}
private void OnContinueClick()
{
if (!_curChapterVo.hasSaveFile)
{
Debug.LogWarning("存档与关联篇章不匹配,请查找问题");
}
chapterController.StartWithSaveFile();
Close();
}
private void OnRestartClick()
{
chapterController.StartWithChapter(_curChapterVo);
Close();
}
#endregion
#region panel属性
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => true;
public void Close()
{
UIManager.Instance.HidePanel<ChapterPanel>();
}
#endregion
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1ebbdd8010b4400089d20c1b556e7b5f
timeCreated: 1756194127
+2 -2
View File
@@ -91,12 +91,12 @@ namespace AibisDream.UI
private void OpenSettingPanel()
{
UIManager.Instance.ShowPreferredSettingPanel();
UIManager.Instance.ShowTerminalSetting();
}
private void OpenRecordPanel()
{
UIManager.Instance.ShowPreferredRecordPanel();
UIManager.Instance.ShowTerminalRecord();
}
}
}
@@ -1,99 +0,0 @@
using System.Collections.Generic;
using AibisDream.Kit;
using AibisDream.Utility;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.UI
{
public class RecordPanel : MonoBehaviour, IUIPanel
{
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => true;
#region
[SerializeField] private Image popupBackground;
[SerializeField] private Animator animator;
[SerializeField] private Button closeBtn;
[SerializeField] private ScrollRect scrollRect;
[SerializeField] private Transform recordRoot;
[SerializeField] private GameObject recordPrefab;
#endregion
private readonly List<LineInfo> _recordDataList = new();
public void Show()
{
GameManager.Instance.PauseGame();
gameObject.SetActive(true);
AddBackground();
animator.Play("Open", 0, 0);
closeBtn.onClick.AddListener(ReturnGame);
// 打开场景
ShowRecord();
}
public void Hide()
{
gameObject.SetActive(false);
GameManager.Instance.ContinueGame();
// 关闭背景
RemoveBackground();
animator.Play("Close", 0, 0);
ActionKit.Delay(ConstRef.PanelDelayDuration, () => popupBackground.gameObject.SetActive(false)).Start(this);
// 关闭时停止渲染
closeBtn.onClick.RemoveAllListeners();
HideRecord();
}
private void AddBackground()
{
popupBackground.canvasRenderer.SetAlpha(0.0f);
popupBackground.gameObject.SetActive(true);
popupBackground.CrossFadeAlpha(1.0f, 0.4f, true);
}
private void RemoveBackground()
{
popupBackground.CrossFadeAlpha(0.0f, 0.2f, false);
}
public void AppendDialogLine(LineInfo lineInfo)
{
_recordDataList.Add(lineInfo);
}
public void ClearDialog()
{
_recordDataList.Clear();
}
private void ShowRecord()
{
foreach (var recordData in _recordDataList)
{
var record = Instantiate(recordPrefab, recordRoot);
record.GetComponent<RecordItem>().Init(recordData);
}
scrollRect.verticalNormalizedPosition = 0;
}
private void HideRecord()
{
// TODO 可能有性能问题
recordRoot.DestroyAllChildren();
}
private void ReturnGame()
{
UIManager.Instance.HidePanel<RecordPanel>();
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: dc6a1b2ccb564028b69d821ab33778f5
timeCreated: 1744779271
-124
View File
@@ -1,124 +0,0 @@
using AibisDream.Kit;
using UnityEngine;
using UnityEngine.UI;
using AibisDream.Utility;
namespace AibisDream.UI
{
public class SettingPanel : MonoBehaviour, IUIPanel
{
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => true;
#region
[SerializeField] private Image background;
[SerializeField] private Animator animator;
[Space]
[SerializeField] private Button continueBtn;
[SerializeField] private Button backToMainBtn;
[SerializeField] private Button quitBtn;
[SerializeField] private Button wishlistBtn;
[SerializeField] private Button bugBtn;
#endregion
#region
public void Show()
{
GameManager.Instance.PauseGame();
gameObject.SetActive(true);
AddBackground();
animator.Play("Open", 0, 0);
// 更新表单内容
var form = GetComponentInChildren<SettingForm>();
form.Init();
// 注册函数
RegisterButton();
}
public void Hide()
{
// 更新表单内容
var form = GetComponentInChildren<SettingForm>();
form.UnInit();
UnRegisterButton();
RemoveBackground();
animator.Play("Close", 0, 0);
GameManager.Instance.ContinueGame();
ActionKit.Delay(ConstRef.PanelDelayDuration, () =>
{
gameObject.SetActive(false);
// 继续游戏
}).Start(this);
}
#endregion
private void AddBackground()
{
background.canvasRenderer.SetAlpha(0.0f);
background.gameObject.SetActive(true);
background.CrossFadeAlpha(1.0f, 0.4f, true);
}
private void RemoveBackground()
{
background.CrossFadeAlpha(0.0f, 0.2f, false);
}
private void RegisterButton()
{
continueBtn.onClick.AddListener(BackToGame);
backToMainBtn.onClick.AddListener(BackToMain);
quitBtn.onClick.AddListener(Quit);
wishlistBtn.onClick.AddListener(OpenWishlist);
bugBtn.onClick.AddListener(OpenBugReport);
}
private void UnRegisterButton()
{
continueBtn.onClick.RemoveAllListeners();
backToMainBtn.onClick.RemoveAllListeners();
quitBtn.onClick.RemoveAllListeners();
wishlistBtn.onClick.RemoveAllListeners();
bugBtn.onClick.RemoveAllListeners();
}
private void BackToGame()
{
// 关闭Setting面板
UIManager.Instance.HidePanel<SettingPanel>();
}
private void BackToMain()
{
// 关闭Setting面板
UIManager.Instance.HidePanel<SettingPanel>();
// 重启
GameManager.Instance.QuitGame();
}
private void Quit()
{
Application.Quit();
}
private void OpenWishlist()
{
Application.OpenURL(ConstRef.WishlistURL);
}
private void OpenBugReport()
{
Application.OpenURL(ConstRef.BugSurveyURL);
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ff78a14b28070864692582ecc5db824c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
-90
View File
@@ -1,90 +0,0 @@
using AibisDream.Utility;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.UI
{
public class StartPanel : MonoBehaviour, IUIPanel
{
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => false;
#region
public void Show()
{
gameObject.SetActive(true);
}
public void Hide()
{
gameObject.SetActive(false);
}
#endregion
private void Awake()
{
RegisterButton();
}
private void RegisterButton()
{
// 开始界面按钮
var tabs = transform.Find("Tabs");
tabs.Find("Start").GetComponent<Button>().onClick.AddListener(StartNewGame);
tabs.Find("Load Save File").GetComponent<Button>().onClick.AddListener(OpenSaveFilePanel);
tabs.Find("Setting").GetComponent<Button>().onClick.AddListener(OpenSetting);
tabs.Find("Quit").GetComponent<Button>().onClick.AddListener(QuitApp);
// 语言切换按钮
var language = transform.Find("Language");
language.Find("Chinese").GetComponent<Button>().onClick.AddListener(() => SetLanguage("zh-Hans"));
language.Find("English").GetComponent<Button>().onClick.AddListener(() => SetLanguage("en"));
language.Find("Japanese").GetComponent<Button>().onClick.AddListener(() => SetLanguage("ja-JP"));
// 其他链接按钮
var others = transform.Find("Others");
others.Find("问卷").GetComponent<Button>().onClick.AddListener(() =>
{
SettingLoader.Instance.TryRead("Language", out var currentLanguage);
var url = currentLanguage == "zh-Hans" ? ConstRef.SurveyURL_CN : ConstRef.SurveyURL_EN;
Application.OpenURL(url);
});
others.Find("关注").GetComponent<Button>().onClick.AddListener(() =>
{
SettingLoader.Instance.TryRead("Language", out var currentLanguage);
var url = currentLanguage == "zh-Hans" ? ConstRef.QQURL : ConstRef.DiscordURL;
Application.OpenURL(url);
});
others.Find("愿望单").GetComponent<Button>().onClick.AddListener(() => Application.OpenURL(ConstRef.WishlistURL));
}
#region
private void SetLanguage(string language)
{
SettingLoader.Instance.Write("Language", language);
}
private void StartNewGame()
{
GameManager.Instance.StartNewGame();
}
private void OpenSaveFilePanel()
{
UIManager.Instance.ShowPreferredChapterPanel();
}
private void OpenSetting()
{
UIManager.Instance.ShowPreferredSettingPanel();
}
private void QuitApp()
{
GameManager.Instance.QuitApp();
}
#endregion
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9ba6256b10e589d478fc8c1910595417
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -14,6 +14,8 @@ namespace AibisDream.UI.Terminal
[SerializeField] private TMP_Text statusText;
[SerializeField] private Image selectionFrame;
[SerializeField] private CanvasGroup canvasGroup;
[SerializeField] private float selectedScale = 1.05f;
[SerializeField] private float previewScale = 1f;
private BaseButton _button;
private ChapterVo _chapterVo;
@@ -65,18 +67,21 @@ namespace AibisDream.UI.Terminal
if (statusText != null)
{
statusText.text = chapterVo.hasSaveFile ? "CHECK OK" : "PENDING";
statusText.gameObject.SetActive(false);
}
if (selectionFrame != null)
{
selectionFrame.gameObject.SetActive(selected);
selectionFrame.gameObject.SetActive(false);
}
if (canvasGroup != null)
{
canvasGroup.alpha = preview ? 0.45f : 1f;
canvasGroup.alpha = 1f;
}
var scaleMultiplier = selected ? selectedScale : previewScale;
transform.localScale = Vector3.one * scaleMultiplier;
}
}
}
@@ -5,10 +5,9 @@ using UnityEngine.UI;
namespace AibisDream.UI.Terminal
{
public class TerminalChapterPanel : MonoBehaviour, IUIPanel
public class TerminalChapterPanel : MonoBehaviour
{
[SerializeField] private TerminalPanelAnimator panelAnimator;
[SerializeField] private TerminalHeaderView header;
[SerializeField] private TerminalChapterItemView leftPreview;
[SerializeField] private TerminalChapterItemView currentView;
[SerializeField] private TerminalChapterItemView rightPreview;
@@ -16,7 +15,6 @@ namespace AibisDream.UI.Terminal
[SerializeField] private TMP_Text selectedTitleText;
[SerializeField] private Button previousButton;
[SerializeField] private Button nextButton;
[SerializeField] private Button continueButton;
[SerializeField] private Button restartButton;
[SerializeField] private Button backButton;
@@ -24,13 +22,17 @@ namespace AibisDream.UI.Terminal
private int _currentIndex;
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => true;
private TerminalPanel _owner;
public void SetOwner(TerminalPanel owner)
{
_owner = owner;
}
private void Awake()
{
previousButton?.onClick.AddListener(Previous);
nextButton?.onClick.AddListener(Next);
continueButton?.onClick.AddListener(Continue);
restartButton?.onClick.AddListener(Restart);
backButton?.onClick.AddListener(Close);
}
@@ -39,23 +41,22 @@ namespace AibisDream.UI.Terminal
{
previousButton?.onClick.RemoveListener(Previous);
nextButton?.onClick.RemoveListener(Next);
continueButton?.onClick.RemoveListener(Continue);
restartButton?.onClick.RemoveListener(Restart);
backButton?.onClick.RemoveListener(Close);
}
public void Show()
{
header?.SetText("<选择章节>", string.Empty);
_owner?.SetHeader("<选择章节>");
LoadChapters();
RefreshViews();
if (panelAnimator != null)
{
panelAnimator.Show();
panelAnimator.Show(RefreshViews);
}
else
{
gameObject.SetActive(true);
RefreshViews();
}
}
@@ -73,7 +74,7 @@ namespace AibisDream.UI.Terminal
public void Close()
{
UIManager.Instance.HidePreferredChapterPanel();
_owner.Back();
}
private void LoadChapters()
@@ -92,9 +93,12 @@ namespace AibisDream.UI.Terminal
emptyText.gameObject.SetActive(!hasChapters);
}
SetViewActive(leftPreview, hasChapters && _chapters.Count > 1);
var showLeftPreview = hasChapters && _currentIndex > 0;
var showRightPreview = hasChapters && _currentIndex < _chapters.Count - 1;
SetViewActive(leftPreview, showLeftPreview);
SetViewActive(currentView, hasChapters);
SetViewActive(rightPreview, hasChapters && _chapters.Count > 1);
SetViewActive(rightPreview, showRightPreview);
if (!hasChapters)
{
@@ -103,23 +107,44 @@ namespace AibisDream.UI.Terminal
selectedTitleText.text = "NO CHAPTER DATA";
}
continueButton?.gameObject.SetActive(false);
restartButton?.gameObject.SetActive(false);
UpdateNavigationButtons(false);
return;
}
var current = _chapters[_currentIndex];
currentView?.Init(current, selected: true, preview: false);
leftPreview?.Init(_chapters[LoopIndex(_currentIndex - 1)], selected: false, preview: true);
rightPreview?.Init(_chapters[LoopIndex(_currentIndex + 1)], selected: false, preview: true);
if (showLeftPreview)
{
leftPreview?.Init(_chapters[_currentIndex - 1], selected: false, preview: true);
}
if (showRightPreview)
{
rightPreview?.Init(_chapters[_currentIndex + 1], selected: false, preview: true);
}
if (selectedTitleText != null)
{
selectedTitleText.text = current.title;
}
continueButton?.gameObject.SetActive(current.hasSaveFile);
restartButton?.gameObject.SetActive(true);
UpdateNavigationButtons(true);
}
private void UpdateNavigationButtons(bool hasChapters)
{
if (previousButton != null)
{
previousButton.interactable = hasChapters && _currentIndex > 0;
}
if (nextButton != null)
{
nextButton.interactable = hasChapters && _currentIndex < _chapters.Count - 1;
}
}
private static void SetViewActive(TerminalChapterItemView view, bool active)
@@ -130,49 +155,28 @@ namespace AibisDream.UI.Terminal
}
}
private int LoopIndex(int index)
{
if (_chapters.Count == 0)
{
return 0;
}
return (index + _chapters.Count) % _chapters.Count;
}
private void Previous()
{
if (_chapters.Count == 0)
if (_chapters.Count == 0 || _currentIndex <= 0)
{
return;
}
_currentIndex = LoopIndex(_currentIndex - 1);
_currentIndex--;
RefreshViews();
}
private void Next()
{
if (_chapters.Count == 0)
if (_chapters.Count == 0 || _currentIndex >= _chapters.Count - 1)
{
return;
}
_currentIndex = LoopIndex(_currentIndex + 1);
_currentIndex++;
RefreshViews();
}
private void Continue()
{
if (_chapters.Count == 0 || !_chapters[_currentIndex].hasSaveFile)
{
return;
}
ChapterController.Instance.StartWithSaveFile();
Close();
}
private void Restart()
{
if (_chapters.Count == 0)
@@ -180,8 +184,8 @@ namespace AibisDream.UI.Terminal
return;
}
ChapterController.Instance.StartWithChapter(_chapters[_currentIndex]);
Close();
var chapter = _chapters[_currentIndex];
_owner.CloseThen(() => ChapterController.Instance.StartWithChapter(chapter));
}
}
}
+252
View File
@@ -0,0 +1,252 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace AibisDream.UI.Terminal
{
public enum TerminalPage
{
None,
Start,
Setting,
Chapter,
Record
}
public enum TerminalOpenSource
{
None,
MainMenu,
InGame
}
public class TerminalPanel : MonoBehaviour, IUIPanel
{
[SerializeField] private TerminalHeaderView header;
[SerializeField] private TerminalStartPanel startPage;
[SerializeField] private TerminalSettingPanel settingPage;
[SerializeField] private TerminalChapterPanel chapterPage;
[SerializeField] private TerminalRecordPanel recordPage;
private readonly Stack<TerminalPage> _backStack = new();
private TerminalPage _currentPage = TerminalPage.None;
private TerminalOpenSource _source = TerminalOpenSource.None;
private bool _pausedByTerminal;
public bool IsOpen => gameObject.activeSelf && _currentPage != TerminalPage.None;
public bool IsCloseable => _source == TerminalOpenSource.InGame;
public TerminalPage CurrentPage => _currentPage;
public TerminalOpenSource Source => _source;
public void Initialize(RectTransform searchRoot)
{
startPage ??= searchRoot.GetComponentInChildren<TerminalStartPanel>(true);
settingPage ??= searchRoot.GetComponentInChildren<TerminalSettingPanel>(true);
chapterPage ??= searchRoot.GetComponentInChildren<TerminalChapterPanel>(true);
recordPage ??= searchRoot.GetComponentInChildren<TerminalRecordPanel>(true);
startPage?.SetOwner(this);
settingPage?.SetOwner(this);
chapterPage?.SetOwner(this);
recordPage?.SetOwner(this);
Close();
}
public bool HasRequiredPages()
{
return startPage != null && settingPage != null && chapterPage != null && recordPage != null;
}
public void Show()
{
Open(TerminalPage.Start, TerminalOpenSource.MainMenu);
}
public void Hide()
{
Close();
}
public void Open(TerminalPage page, TerminalOpenSource source)
{
if (!HasRequiredPages())
{
return;
}
if (!IsOpen || _source != source)
{
_backStack.Clear();
}
gameObject.SetActive(true);
_source = source;
PauseIfNeeded(page, source);
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);
}
HidePage(_currentPage);
_currentPage = page;
ShowPage(_currentPage);
}
public void Back()
{
if (_source == TerminalOpenSource.InGame)
{
Close();
return;
}
if (_backStack.Count > 0)
{
var previous = _backStack.Pop();
HidePage(_currentPage);
_currentPage = previous;
ShowPage(_currentPage);
return;
}
if (_currentPage != TerminalPage.Start)
{
SwitchTo(TerminalPage.Start, pushCurrent: false);
}
}
public bool HandleEscape()
{
if (!IsOpen)
{
return false;
}
if (_currentPage == TerminalPage.Start)
{
return true;
}
Back();
return true;
}
public void CloseThen(Action action)
{
Close();
action?.Invoke();
}
public void Close()
{
HideAllPages();
_backStack.Clear();
_currentPage = TerminalPage.None;
_source = TerminalOpenSource.None;
ContinueIfPaused();
gameObject.SetActive(false);
}
public void CloseToMainMenu()
{
Close();
GameManager.Instance.QuitGame();
}
public void AppendDialogLine(LineInfo lineInfo)
{
recordPage?.AppendDialogLine(lineInfo);
}
public void ClearDialog()
{
recordPage?.ClearDialog();
}
public void SetHeader(string title, string status = "")
{
header?.SetText(title, status);
}
private void PauseIfNeeded(TerminalPage page, TerminalOpenSource source)
{
if (source != TerminalOpenSource.InGame || page is not (TerminalPage.Setting or TerminalPage.Record))
{
return;
}
if (!_pausedByTerminal)
{
GameManager.Instance.PauseGame();
_pausedByTerminal = true;
}
}
private void ContinueIfPaused()
{
if (!_pausedByTerminal)
{
return;
}
GameManager.Instance.ContinueGame();
_pausedByTerminal = false;
}
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;
case TerminalPage.Record:
recordPage?.Show();
break;
}
}
private void HidePage(TerminalPage page)
{
switch (page)
{
case TerminalPage.Start:
startPage?.Hide();
break;
case TerminalPage.Setting:
settingPage?.Hide();
break;
case TerminalPage.Chapter:
chapterPage?.Hide();
break;
case TerminalPage.Record:
recordPage?.Hide();
break;
}
}
private void HideAllPages()
{
startPage?.Hide();
settingPage?.Hide();
chapterPage?.Hide();
recordPage?.Hide();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2f574a12df693144e9f80929d52ca9ef
@@ -6,10 +6,9 @@ using UnityEngine.UI;
namespace AibisDream.UI.Terminal
{
public class TerminalRecordPanel : MonoBehaviour, IUIPanel
public class TerminalRecordPanel : MonoBehaviour
{
[SerializeField] private TerminalPanelAnimator panelAnimator;
[SerializeField] private TerminalHeaderView header;
[SerializeField] private Button backButton;
[SerializeField] private ScrollRect scrollRect;
[SerializeField] private Transform recordRoot;
@@ -19,7 +18,12 @@ namespace AibisDream.UI.Terminal
private readonly List<LineInfo> _recordDataList = new List<LineInfo>();
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => true;
private TerminalPanel _owner;
public void SetOwner(TerminalPanel owner)
{
_owner = owner;
}
private void Awake()
{
@@ -33,8 +37,7 @@ namespace AibisDream.UI.Terminal
public void Show()
{
GameManager.Instance.PauseGame();
header?.SetText("<诊疗记录>", "EMR 511/42 - Clinical Record");
_owner?.SetHeader("<诊疗记录>", "EMR 511/42 - Clinical Record");
DrawRecords();
if (panelAnimator != null)
{
@@ -58,7 +61,6 @@ namespace AibisDream.UI.Terminal
gameObject.SetActive(false);
}
GameManager.Instance.ContinueGame();
}
public void AppendDialogLine(LineInfo lineInfo)
@@ -100,9 +102,9 @@ namespace AibisDream.UI.Terminal
}
}
private static void ReturnGame()
private void ReturnGame()
{
UIManager.Instance.HidePreferredRecordPanel();
_owner.Back();
}
}
}
@@ -1,28 +1,26 @@
using UnityEngine;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.UI.Terminal
{
public class TerminalSettingPanel : MonoBehaviour, IUIPanel
public class TerminalSettingPanel : MonoBehaviour
{
[SerializeField] private TerminalPanelAnimator panelAnimator;
[SerializeField] private TerminalHeaderView header;
[SerializeField] private TerminalSliderView volumeSlider;
[SerializeField] private TerminalSliderView textSpeedSlider;
[SerializeField] private TerminalOptionItemView windowModeOption;
[SerializeField] private TerminalOptionItemView languageOption;
[SerializeField] private SliderItem volumeSlider;
[SerializeField] private SelectItem textSpeedOption;
[SerializeField] private SelectItem windowModeOption;
[SerializeField] private SelectItem languageOption;
[SerializeField] private Button continueButton;
[SerializeField] private Button backToMainButton;
[SerializeField] private Button quitButton;
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => true;
private TerminalPanel _owner;
private static readonly UIFormOption[] WindowModeOptions =
{
new UIFormOption { prop = "FullScreen", label = "全屏" },
new UIFormOption { prop = "Windowed", label = "窗口" },
new UIFormOption { prop = "MaximizedWindow", label = "无边框" },
new UIFormOption { prop = "FullScreen", label = "setting_window_fullscreen" },
new UIFormOption { prop = "Windowed", label = "setting_window_windowed" },
};
private static readonly UIFormOption[] LanguageOptions =
@@ -32,6 +30,18 @@ namespace AibisDream.UI.Terminal
new UIFormOption { prop = "ja-JP", label = "日本語" },
};
private static readonly UIFormOption[] TextSpeedOptions =
{
new UIFormOption { prop = "0.5", label = "setting_textSpeed_slow" },
new UIFormOption { prop = "1.0", label = "setting_textSpeed_default" },
new UIFormOption { prop = "3.0", label = "setting_textSpeed_fast" },
};
public void SetOwner(TerminalPanel owner)
{
_owner = owner;
}
private void Awake()
{
continueButton?.onClick.AddListener(BackToGame);
@@ -48,10 +58,10 @@ namespace AibisDream.UI.Terminal
public void Show()
{
GameManager.Instance.PauseGame();
header?.SetText("<设置>", "MUSIC 50/100 - AUDIO INPUT 001");
_owner?.SetHeader("<设置>", "MUSIC 50/100 - AUDIO INPUT 001");
InitControls();
RefreshControls();
if (panelAnimator != null)
{
panelAnimator.Show();
@@ -72,14 +82,12 @@ namespace AibisDream.UI.Terminal
{
gameObject.SetActive(false);
}
GameManager.Instance.ContinueGame();
}
private void InitControls()
{
volumeSlider?.Init("Volume", "音量.....", 0f, 1f, OnSettingChanged);
textSpeedSlider?.Init("TextSpeed", "文字速度", 0.5f, 8f, OnSettingChanged);
textSpeedOption?.Init("TextSpeed", "文字速度", TextSpeedOptions, OnSettingChanged);
windowModeOption?.Init("WindowMode", "屏幕模式", WindowModeOptions, OnSettingChanged);
languageOption?.Init("Language", "语言.....", LanguageOptions, OnSettingChanged);
}
@@ -94,7 +102,7 @@ namespace AibisDream.UI.Terminal
if (settings.TryGetValue("TextSpeed", out var textSpeed))
{
textSpeedSlider?.SetValue(textSpeed);
textSpeedOption?.SetValue(textSpeed);
}
if (settings.TryGetValue("WindowMode", out var windowMode))
@@ -113,15 +121,21 @@ namespace AibisDream.UI.Terminal
SettingLoader.Instance.Write(propName, value);
}
private static void BackToGame()
private void BackToGame()
{
UIManager.Instance.HidePreferredSettingPanel();
if (_owner.Source == TerminalOpenSource.InGame)
{
_owner.Close();
}
else
{
_owner.Back();
}
}
private static void BackToMain()
private void BackToMain()
{
UIManager.Instance.HidePreferredSettingPanel();
GameManager.Instance.QuitGame();
_owner.CloseToMainMenu();
}
private static void QuitApp()
@@ -78,6 +78,15 @@ namespace AibisDream.UI.Terminal
{
valueText.text = $"{Mathf.RoundToInt(value * 100)}/100";
}
else if (_propName == "TextSpeed")
{
valueText.text = value switch
{
< 1.5f => "<慢速>",
< 4.5f => "<正常>",
_ => "<快速>"
};
}
else
{
valueText.text = value.ToString("0.##", CultureInfo.InvariantCulture);
@@ -3,20 +3,24 @@ using UnityEngine;
namespace AibisDream.UI.Terminal
{
public class TerminalStartPanel : MonoBehaviour, IUIPanel
public class TerminalStartPanel : MonoBehaviour
{
[SerializeField] private TerminalPanelAnimator panelAnimator;
[SerializeField] private TerminalHeaderView header;
[SerializeField] private TerminalMenuItemView newGameItem;
[SerializeField] private TerminalMenuItemView chapterItem;
[SerializeField] private TerminalMenuItemView settingItem;
[SerializeField] private TerminalMenuItemView quitItem;
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => false;
private TerminalPanel _owner;
private TerminalMenuItemView _selectedItem;
public bool IsOpen => gameObject.activeSelf;
public void SetOwner(TerminalPanel owner)
{
_owner = owner;
}
private void Awake()
{
RegisterItem(newGameItem, StartNewGame);
@@ -35,12 +39,7 @@ namespace AibisDream.UI.Terminal
public void Show()
{
// header?.SetText("<选择章节>", "CHAPER xx/xxx - LOADING - DT");
// newGameItem?.SetContent("新游戏.....", "pending", "Current date: 04/2020");
// chapterItem?.SetContent("选择章节....", "check ok", "Current date: 00/2020");
// settingItem?.SetContent("设置.......", "pending", "Current date: xx/xxxx");
// quitItem?.SetContent("退出.......", "-----", "-----: 00/0000", isDanger: true);
// SelectItem(chapterItem != null ? chapterItem : newGameItem);
_owner?.SetHeader("<选择章节>", "CHAPER xx/xxx - LOADING - DT");
if (panelAnimator != null)
{
panelAnimator.Show();
@@ -97,17 +96,17 @@ namespace AibisDream.UI.Terminal
private static void StartNewGame()
{
GameManager.Instance.StartNewGame();
UIManager.Instance.CloseTerminalThen(() => GameManager.Instance.StartNewGame());
}
private static void OpenChapter()
private void OpenChapter()
{
UIManager.Instance.ShowPreferredChapterPanel();
_owner.SwitchTo(TerminalPage.Chapter);
}
private static void OpenSetting()
private void OpenSetting()
{
UIManager.Instance.ShowPreferredSettingPanel();
_owner.SwitchTo(TerminalPage.Setting);
}
private static void QuitApp()
+87 -83
View File
@@ -24,6 +24,7 @@ namespace AibisDream
public RectTransform DialogCanvas { get; private set; }
private GraphicRaycaster _raycaster;
private TerminalPanel _terminalPanel;
#endregion
@@ -36,17 +37,14 @@ namespace AibisDream
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (IsPreferredSettingOpen())
if (_terminalPanel != null && _terminalPanel.HandleEscape())
{
HidePreferredSettingPanel();
return;
}
else if (IsPreferredRecordOpen())
if (GameManager.Instance != null && GameManager.Instance.state.isInGame)
{
HidePreferredRecordPanel();
}
else if (GameManager.Instance != null && GameManager.Instance.state.isInGame)
{
ShowPreferredSettingPanel();
ShowTerminalSetting();
}
}
@@ -79,34 +77,64 @@ namespace AibisDream
_panelPool[panel.GetType()] = panel;
}
RegisterTerminalPanel();
EnumEventSystem.Global.Register(GameLoopEnum.AppStart, OnAppStart);
EnumEventSystem.Global.Register(GameLoopEnum.GameStart, OnGameStart);
EnumEventSystem.Global.Register(GameLoopEnum.GameQuit, OnGameQuit);
}
private void RegisterTerminalPanel()
{
if (_panelPool.TryGetValue(typeof(TerminalPanel), out var registeredPanel))
{
_terminalPanel = registeredPanel as TerminalPanel;
}
else
{
_terminalPanel = Canvas.GetComponentInChildren<TerminalPanel>(true);
}
if (_terminalPanel == null)
{
Debug.LogError("[UIManager] TerminalPanel not found under UI Canvas.");
return;
}
_terminalPanel.Initialize(Canvas);
if (_terminalPanel.HasRequiredPages())
{
_panelPool[typeof(TerminalPanel)] = _terminalPanel;
}
else
{
Debug.LogError("[UIManager] TerminalPanel is missing required pages.");
}
}
private void OnAppStart()
{
// 游戏打开
ShowPreferredStartPanel();
ShowTerminalStart();
ShowPanel<PlayToolPanel>();
HidePanel<MainPanel>();
HidePreferredSettingPanel();
HidePreferredRecordPanel();
HideTerminalSetting();
HideTerminalRecord();
HidePanel<EndPanel>();
}
private void OnGameStart()
{
// 开始游戏
HidePreferredStartPanel();
HideTerminal();
ShowPanel<MainPanel>();
}
private void OnGameQuit()
{
HidePanel<MainPanel>();
ShowPreferredStartPanel();
ShowTerminalStart();
}
public void ShowPanel<T>(Action<T> call = null) where T : class, IUIPanel
@@ -183,92 +211,80 @@ namespace AibisDream
return panel != null;
}
public void ShowPreferredStartPanel()
public void ShowTerminalStart()
{
if (TryGetPanel<TerminalStartPanel>(out _))
GetPanel<TerminalPanel>()?.Open(TerminalPage.Start, TerminalOpenSource.MainMenu);
}
public void HideTerminal()
{
GetPanel<TerminalPanel>()?.Close();
}
public void ShowTerminalSetting()
{
var source = GameManager.Instance != null && GameManager.Instance.state.isInGame
? TerminalOpenSource.InGame
: TerminalOpenSource.MainMenu;
GetPanel<TerminalPanel>()?.Open(TerminalPage.Setting, source);
}
public void HideTerminalSetting()
{
var terminal = GetPanel<TerminalPanel>();
if (terminal == null || !terminal.IsOpen || terminal.CurrentPage != TerminalPage.Setting)
{
ShowPanel<TerminalStartPanel>();
return;
}
if (terminal.Source == TerminalOpenSource.InGame)
{
terminal.Close();
}
else
{
ShowPanel<StartPanel>();
terminal.Back();
}
}
public void HidePreferredStartPanel()
public void ShowTerminalChapter()
{
if (TryGetPanel<TerminalStartPanel>(out _))
{
HidePanel<TerminalStartPanel>();
}
HidePanel<StartPanel>();
GetPanel<TerminalPanel>()?.Open(TerminalPage.Chapter, TerminalOpenSource.MainMenu);
}
public void ShowPreferredSettingPanel()
public void HideTerminalChapter()
{
if (TryGetPanel<TerminalSettingPanel>(out _))
var terminal = GetPanel<TerminalPanel>();
if (terminal != null && terminal.IsOpen && terminal.CurrentPage == TerminalPage.Chapter)
{
ShowPanel<TerminalSettingPanel>();
}
else
{
ShowPanel<SettingPanel>();
terminal.Back();
}
}
public void HidePreferredSettingPanel()
public void ShowTerminalRecord()
{
if (TryGetPanel<TerminalSettingPanel>(out _))
{
HidePanel<TerminalSettingPanel>();
}
HidePanel<SettingPanel>();
GetPanel<TerminalPanel>()?.Open(TerminalPage.Record, TerminalOpenSource.InGame);
}
public void ShowPreferredChapterPanel()
public void HideTerminalRecord()
{
if (TryGetPanel<TerminalChapterPanel>(out _))
var terminal = GetPanel<TerminalPanel>();
if (terminal != null && terminal.IsOpen && terminal.CurrentPage == TerminalPage.Record)
{
ShowPanel<TerminalChapterPanel>();
}
else
{
ShowPanel<ChapterPanel>();
terminal.Back();
}
}
public void HidePreferredChapterPanel()
public void CloseTerminalThen(Action action)
{
if (TryGetPanel<TerminalChapterPanel>(out _))
var terminal = GetPanel<TerminalPanel>();
if (terminal != null && terminal.IsOpen)
{
HidePanel<TerminalChapterPanel>();
terminal.CloseThen(action);
return;
}
HidePanel<ChapterPanel>();
}
public void ShowPreferredRecordPanel()
{
if (TryGetPanel<TerminalRecordPanel>(out _))
{
ShowPanel<TerminalRecordPanel>();
}
else
{
ShowPanel<RecordPanel>();
}
}
public void HidePreferredRecordPanel()
{
if (TryGetPanel<TerminalRecordPanel>(out _))
{
HidePanel<TerminalRecordPanel>();
}
HidePanel<RecordPanel>();
action?.Invoke();
}
public void ShowPreferredLoading()
@@ -302,18 +318,6 @@ namespace AibisDream
GetPanel<InfoPanel>()?.HideLoading();
}
private bool IsPreferredSettingOpen()
{
return (TryGetPanel<TerminalSettingPanel>(out var terminalSetting) && terminalSetting.IsOpen)
|| (TryGetPanel<SettingPanel>(out var setting) && setting.IsOpen);
}
private bool IsPreferredRecordOpen()
{
return (TryGetPanel<TerminalRecordPanel>(out var terminalRecord) && terminalRecord.IsOpen)
|| (TryGetPanel<RecordPanel>(out var record) && record.IsOpen);
}
public static Vector3 GetDialogUIPos(Vector3 screenPos)
{
// 转换为UI坐标