feat(ui): 添加 Terminal 风格主界面 UI 系统

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-01 12:06:11 +08:00
co-authored by Cursor
parent da436479db
commit a05d5d476c
63 changed files with 15763 additions and 3481 deletions
@@ -0,0 +1,187 @@
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.UI.Terminal
{
public class TerminalChapterPanel : MonoBehaviour, IUIPanel
{
[SerializeField] private TerminalPanelAnimator panelAnimator;
[SerializeField] private TerminalHeaderView header;
[SerializeField] private TerminalChapterItemView leftPreview;
[SerializeField] private TerminalChapterItemView currentView;
[SerializeField] private TerminalChapterItemView rightPreview;
[SerializeField] private TMP_Text emptyText;
[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;
private readonly List<ChapterVo> _chapters = new List<ChapterVo>();
private int _currentIndex;
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => true;
private void Awake()
{
previousButton?.onClick.AddListener(Previous);
nextButton?.onClick.AddListener(Next);
continueButton?.onClick.AddListener(Continue);
restartButton?.onClick.AddListener(Restart);
backButton?.onClick.AddListener(Close);
}
private void OnDestroy()
{
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);
LoadChapters();
RefreshViews();
if (panelAnimator != null)
{
panelAnimator.Show();
}
else
{
gameObject.SetActive(true);
}
}
public void Hide()
{
if (panelAnimator != null)
{
panelAnimator.Hide();
}
else
{
gameObject.SetActive(false);
}
}
public void Close()
{
UIManager.Instance.HidePreferredChapterPanel();
}
private void LoadChapters()
{
_chapters.Clear();
var chapters = ChapterController.Instance.GetChapterList();
_chapters.AddRange(chapters);
_currentIndex = Mathf.Clamp(_currentIndex, 0, Mathf.Max(0, _chapters.Count - 1));
}
private void RefreshViews()
{
var hasChapters = _chapters.Count > 0;
if (emptyText != null)
{
emptyText.gameObject.SetActive(!hasChapters);
}
SetViewActive(leftPreview, hasChapters && _chapters.Count > 1);
SetViewActive(currentView, hasChapters);
SetViewActive(rightPreview, hasChapters && _chapters.Count > 1);
if (!hasChapters)
{
if (selectedTitleText != null)
{
selectedTitleText.text = "NO CHAPTER DATA";
}
continueButton?.gameObject.SetActive(false);
restartButton?.gameObject.SetActive(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 (selectedTitleText != null)
{
selectedTitleText.text = current.title;
}
continueButton?.gameObject.SetActive(current.hasSaveFile);
restartButton?.gameObject.SetActive(true);
}
private static void SetViewActive(TerminalChapterItemView view, bool active)
{
if (view != null)
{
view.gameObject.SetActive(active);
}
}
private int LoopIndex(int index)
{
if (_chapters.Count == 0)
{
return 0;
}
return (index + _chapters.Count) % _chapters.Count;
}
private void Previous()
{
if (_chapters.Count == 0)
{
return;
}
_currentIndex = LoopIndex(_currentIndex - 1);
RefreshViews();
}
private void Next()
{
if (_chapters.Count == 0)
{
return;
}
_currentIndex = LoopIndex(_currentIndex + 1);
RefreshViews();
}
private void Continue()
{
if (_chapters.Count == 0 || !_chapters[_currentIndex].hasSaveFile)
{
return;
}
ChapterController.Instance.StartWithSaveFile();
Close();
}
private void Restart()
{
if (_chapters.Count == 0)
{
return;
}
ChapterController.Instance.StartWithChapter(_chapters[_currentIndex]);
Close();
}
}
}