From 3e2d1e931adf66f7c2bb3b6ab94a9b67c9e83cc5 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Thu, 2 Jul 2026 02:44:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E9=87=8D=E6=9E=84=E7=BB=88?= =?UTF-8?q?=E7=AB=AF=E9=9D=A2=E6=9D=BF=EF=BC=8C=E6=8B=86=E5=88=86=E4=B8=BB?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E4=B8=8E=E5=B1=80=E5=86=85=E7=BB=88=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SaveSystem/SaveRestoreOrchestrator.cs | 2 +- Assets/Scripts/UI/DialogUI/DialogUIManager.cs | 7 +- .../Scripts/UI/Terminal/ITerminalPanelHost.cs | 11 + .../UI/Terminal/ITerminalPanelHost.cs.meta | 11 + .../UI/Terminal/InGameTerminalPanel.cs | 212 ++++++++++++++++++ .../UI/Terminal/InGameTerminalPanel.cs.meta | 11 + Assets/Scripts/UI/Terminal/TerminalButton.cs | 144 ++++++++++++ .../UI/Terminal/TerminalButton.cs.meta | 11 + .../UI/Terminal/TerminalChapterItemView.cs | 10 +- .../UI/Terminal/TerminalChapterPanel.cs | 21 +- .../Scripts/UI/Terminal/TerminalHeaderView.cs | 45 +++- .../UI/Terminal/TerminalLoadingPanel.cs | 50 +++-- .../UI/Terminal/TerminalMenuItemView.cs | 50 ++++- .../UI/Terminal/TerminalOptionItemView.cs | 2 +- Assets/Scripts/UI/Terminal/TerminalPanel.cs | 146 +++++------- .../UI/Terminal/TerminalPanelAnimator.cs | 40 ++-- .../UI/Terminal/TerminalRecordItemView.cs | 4 +- .../UI/Terminal/TerminalRecordPanel.cs | 117 +++++++++- .../UI/Terminal/TerminalSettingPanel.cs | 115 +++++++--- .../Scripts/UI/Terminal/TerminalSliderView.cs | 2 +- .../Scripts/UI/Terminal/TerminalStartPanel.cs | 20 +- Assets/Scripts/UI/UIManager.cs | 79 +++++-- 22 files changed, 896 insertions(+), 214 deletions(-) create mode 100644 Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs create mode 100644 Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs.meta create mode 100644 Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs create mode 100644 Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs.meta create mode 100644 Assets/Scripts/UI/Terminal/TerminalButton.cs create mode 100644 Assets/Scripts/UI/Terminal/TerminalButton.cs.meta diff --git a/Assets/Scripts/SaveSystem/SaveRestoreOrchestrator.cs b/Assets/Scripts/SaveSystem/SaveRestoreOrchestrator.cs index 8c2ffa159..6e702e76e 100644 --- a/Assets/Scripts/SaveSystem/SaveRestoreOrchestrator.cs +++ b/Assets/Scripts/SaveSystem/SaveRestoreOrchestrator.cs @@ -3,7 +3,6 @@ using System.Collections; using System.Collections.Generic; using AibisDream.Framework; using AibisDream.UI; -using Newtonsoft.Json.Linq; using UnityEngine; namespace AibisDream.SaveSystem @@ -277,6 +276,7 @@ namespace AibisDream.SaveSystem if (ui != null) { ui.HideTerminal(); + ui.GetPanel()?.Close(); ui.ShowPanel(); ui.HidePanel(); ui.HidePanel(); diff --git a/Assets/Scripts/UI/DialogUI/DialogUIManager.cs b/Assets/Scripts/UI/DialogUI/DialogUIManager.cs index 85b7e059a..f83e393ed 100644 --- a/Assets/Scripts/UI/DialogUI/DialogUIManager.cs +++ b/Assets/Scripts/UI/DialogUI/DialogUIManager.cs @@ -1,7 +1,6 @@ using AibisDream.Framework; using AibisDream.Kit; using AibisDream.UI; -using AibisDream.UI.Terminal; using TMPro; using UnityEngine; @@ -36,12 +35,14 @@ namespace AibisDream return; } - UIManager.Instance.GetPanel()?.AppendDialogLine(line); + var panel = UIManager.Instance.GetPanel(); + panel?.AppendDialogLine(line); } private void CleanDialogHistory() { - UIManager.Instance.GetPanel()?.ClearDialog(); + var panel = UIManager.Instance.GetPanel(); + panel?.ClearDialog(); } public void CloseCanvas() diff --git a/Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs b/Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs new file mode 100644 index 000000000..216fc35a6 --- /dev/null +++ b/Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs @@ -0,0 +1,11 @@ +namespace AibisDream.UI +{ + public interface ITerminalPanelHost + { + void SetHeader(string title, string status = ""); + void Back(); + void Close(); + void CloseToMainMenu(); + bool IsInGameContext { get; } + } +} diff --git a/Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs.meta b/Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs.meta new file mode 100644 index 000000000..351b6f5b4 --- /dev/null +++ b/Assets/Scripts/UI/Terminal/ITerminalPanelHost.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7c6d5e4f3a2918071625344a5b6c7d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs b/Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs new file mode 100644 index 000000000..9148b9fe5 --- /dev/null +++ b/Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace AibisDream.UI +{ + public enum InGameTerminalPage + { + None, + Setting, + Record, + } + + /// + /// 局内终端 Panel 壳:管理 Setting / Record 两个 Page。 + /// Setting / Record 需在场景中分别挂独立的 Prefab 实例,并在 Inspector 中绑定。 + /// + public class InGameTerminalPanel : MonoBehaviour, IUIPanel, ITerminalPanelHost + { + [SerializeField] private TerminalHeaderView header; + [SerializeField] private TerminalSettingPanel settingPage; + [SerializeField] private TerminalRecordPanel recordPage; + + private readonly Stack _backStack = new(); + private InGameTerminalPage _currentPage = InGameTerminalPage.None; + private bool _pausedByPanel; + + public bool IsOpen => gameObject.activeSelf && _currentPage != InGameTerminalPage.None; + public bool IsCloseable => true; + public InGameTerminalPage CurrentPage => _currentPage; + public bool IsInGameContext => true; + + public void Initialize() + { + var root = transform; + settingPage ??= root.GetComponentInChildren(true); + recordPage ??= root.GetComponentInChildren(true); + + settingPage?.SetOwner(this); + recordPage?.SetOwner(this); + Close(); + } + + public bool HasRequiredPages() + { + return settingPage != null && recordPage != null; + } + + public void Show() + { + Open(InGameTerminalPage.Setting); + } + + public void Hide() + { + Close(); + } + + public void Open(InGameTerminalPage page) + { + if (!HasRequiredPages() || page == InGameTerminalPage.None) + { + return; + } + + if (!IsOpen) + { + _backStack.Clear(); + } + + gameObject.SetActive(true); + PauseIfNeeded(); + SwitchTo(page, pushCurrent: false); + } + + public void SwitchTo(InGameTerminalPage page, bool pushCurrent = true) + { + if (page == InGameTerminalPage.None || page == _currentPage) + { + return; + } + + if (pushCurrent && _currentPage != InGameTerminalPage.None) + { + _backStack.Push(_currentPage); + } + + var previous = _currentPage; + _currentPage = page; + + if (previous == InGameTerminalPage.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; + } + + Close(); + } + + public bool HandleEscape() + { + if (!IsOpen) + { + return false; + } + + Back(); + return true; + } + + public void Close() + { + HideAllPages(); + _backStack.Clear(); + _currentPage = InGameTerminalPage.None; + ContinueIfPaused(); + gameObject.SetActive(false); + } + + public void CloseToMainMenu() + { + Close(); + GameManager.Instance.QuitGame(); + } + + public void SetHeader(string title, string status = "") + { + header?.SetText(title, status); + } + + public void AppendDialogLine(LineInfo lineInfo) + { + recordPage?.AppendDialogLine(lineInfo); + } + + public void ClearDialog() + { + recordPage?.ClearDialog(); + } + + private void PauseIfNeeded() + { + if (GameManager.Instance == null || !GameManager.Instance.state.isInGame || _pausedByPanel) + { + return; + } + + GameManager.Instance.PauseGame(); + _pausedByPanel = true; + } + + private void ContinueIfPaused() + { + if (!_pausedByPanel) + { + return; + } + + GameManager.Instance.ContinueGame(); + _pausedByPanel = false; + } + + private void ShowPage(InGameTerminalPage page) + { + switch (page) + { + case InGameTerminalPage.Setting: + settingPage?.Show(); + break; + case InGameTerminalPage.Record: + recordPage?.Show(); + break; + } + } + + private void HidePage(InGameTerminalPage page, Action onComplete = null) + { + switch (page) + { + case InGameTerminalPage.Setting: + settingPage?.Hide(onComplete); + break; + case InGameTerminalPage.Record: + recordPage?.Hide(onComplete); + break; + default: + onComplete?.Invoke(); + break; + } + } + + private void HideAllPages() + { + settingPage?.Hide(); + recordPage?.Hide(); + } + } +} diff --git a/Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs.meta b/Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs.meta new file mode 100644 index 000000000..7e17174c3 --- /dev/null +++ b/Assets/Scripts/UI/Terminal/InGameTerminalPanel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8f4a2b1c3d54e6f9a0b1c2d3e4f5a6b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/Terminal/TerminalButton.cs b/Assets/Scripts/UI/Terminal/TerminalButton.cs new file mode 100644 index 000000000..d2dbacf85 --- /dev/null +++ b/Assets/Scripts/UI/Terminal/TerminalButton.cs @@ -0,0 +1,144 @@ +using System; +using AibisDream; +using AibisDream.Utility; +using TMPro; +using UnityEngine; +using UnityEngine.Localization.Components; +using UnityEngine.UI; + +namespace AibisDream.UI +{ + [RequireComponent(typeof(BaseButton))] + public class TerminalButton : MonoBehaviour + { + [SerializeField] private BaseButton button; + [SerializeField] private Image buttonImage; + [SerializeField] private Image backgroundImage; + [SerializeField] private TMP_Text labelText; + [SerializeField] private LocalizeStringEvent labelLocalize; + + [Header("文字")] + [SerializeField] private string defaultLabel = string.Empty; + + [Header("前景色彩(文字 + 按钮图)")] + [SerializeField] private Color normalForegroundColor = Color.white; + [SerializeField] private Color hoverForegroundColor = new Color(0.02f, 0.02f, 0.04f); + + [Header("底图色彩")] + [SerializeField] private Color normalBackgroundColor = new Color(1f, 1f, 1f, 0f); + [SerializeField] private Color hoverBackgroundColor = Color.white; + + [Header("禁用")] + [SerializeField] private Color disabledForegroundColor = new Color(0.78431374f, 0.78431374f, 0.78431374f, 0.5f); + [SerializeField] private Color disabledBackgroundColor = new Color(1f, 1f, 1f, 0.3f); + + + + public BaseButton Button => button != null ? button : (button = GetComponent()); + + private void Awake() + { + button = GetComponent(); + if (buttonImage == null) + { + buttonImage = GetComponent(); + } + + if (labelLocalize == null && labelText != null) + { + labelLocalize = labelText.GetComponent(); + } + + button.onStateTransition.AddListener(OnStateTransition); + + if (!string.IsNullOrEmpty(defaultLabel)) + { + SetLabel(defaultLabel); + } + } + + private void OnEnable() + { + if (button != null) + { + OnStateTransition(button.CurSelectionState, true); + } + } + + private void OnDestroy() + { + if (button != null) + { + button.onStateTransition.RemoveListener(OnStateTransition); + } + } + + public void SetLabel(string value) + { + if (labelLocalize != null && IsLocalizationKey(value)) + { + labelLocalize.enabled = true; + labelLocalize.SetTable(ConstRef.UITextTable); + labelLocalize.SetEntry(value); + return; + } + + if (labelLocalize != null) + { + labelLocalize.enabled = false; + } + + if (labelText != null) + { + labelText.text = value; + } + } + + private void OnStateTransition(SelectionType state, bool instant) + { + switch (state) + { + case SelectionType.Disabled: + ApplyForegroundColor(disabledForegroundColor); + ApplyBackgroundColor(disabledBackgroundColor); + break; + case SelectionType.Highlighted: + case SelectionType.Pressed: + case SelectionType.Selected: + ApplyForegroundColor(hoverForegroundColor); + ApplyBackgroundColor(hoverBackgroundColor); + break; + default: + ApplyForegroundColor(normalForegroundColor); + ApplyBackgroundColor(normalBackgroundColor); + break; + } + } + + private void ApplyForegroundColor(Color color) + { + if (buttonImage != null) + { + buttonImage.color = color; + } + + if (labelText != null) + { + labelText.color = color; + } + } + + private void ApplyBackgroundColor(Color color) + { + if (backgroundImage != null) + { + backgroundImage.color = color; + } + } + + private static bool IsLocalizationKey(string value) + { + return !string.IsNullOrEmpty(value) && value.Contains("_", StringComparison.Ordinal); + } + } +} diff --git a/Assets/Scripts/UI/Terminal/TerminalButton.cs.meta b/Assets/Scripts/UI/Terminal/TerminalButton.cs.meta new file mode 100644 index 000000000..bc1237326 --- /dev/null +++ b/Assets/Scripts/UI/Terminal/TerminalButton.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: febed96817a804e47ace59be0d7c3995 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/Terminal/TerminalChapterItemView.cs b/Assets/Scripts/UI/Terminal/TerminalChapterItemView.cs index 1d4a1841e..bcab5ea9f 100644 --- a/Assets/Scripts/UI/Terminal/TerminalChapterItemView.cs +++ b/Assets/Scripts/UI/Terminal/TerminalChapterItemView.cs @@ -3,7 +3,7 @@ using TMPro; using UnityEngine; using UnityEngine.UI; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { [RequireComponent(typeof(BaseButton))] public class TerminalChapterItemView : MonoBehaviour @@ -14,8 +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; + [SerializeField] private float selectedScale = 1f; + [SerializeField] private float previewScale = 0.45f; private BaseButton _button; private ChapterVo _chapterVo; @@ -52,7 +52,9 @@ namespace AibisDream.UI.Terminal if (coverImage != null) { coverImage.sprite = chapterVo.pic; - coverImage.enabled = chapterVo.pic != null; + var hasCover = chapterVo.pic != null; + coverImage.enabled = hasCover; + coverImage.preserveAspect = true; } if (titleText != null) diff --git a/Assets/Scripts/UI/Terminal/TerminalChapterPanel.cs b/Assets/Scripts/UI/Terminal/TerminalChapterPanel.cs index 22cac3d9b..2d623c867 100644 --- a/Assets/Scripts/UI/Terminal/TerminalChapterPanel.cs +++ b/Assets/Scripts/UI/Terminal/TerminalChapterPanel.cs @@ -1,9 +1,10 @@ +using System; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalChapterPanel : MonoBehaviour { @@ -47,31 +48,37 @@ namespace AibisDream.UI.Terminal public void Show() { - _owner?.SetHeader("<选择章节>"); - LoadChapters(); if (panelAnimator != null) { - panelAnimator.Show(RefreshViews); + panelAnimator.Show(prepare: PrepareContent); } else { + PrepareContent(); gameObject.SetActive(true); - RefreshViews(); } } - public void Hide() + public void Hide(Action onComplete = null) { if (panelAnimator != null) { - panelAnimator.Hide(); + panelAnimator.Hide(onComplete); } else { gameObject.SetActive(false); + onComplete?.Invoke(); } } + private void PrepareContent() + { + _owner?.SetHeader("terminal_header_chapter", "REPAIR_STATION_07 - AUTH_VERIFIED"); + LoadChapters(); + RefreshViews(); + } + public void Close() { _owner.Back(); diff --git a/Assets/Scripts/UI/Terminal/TerminalHeaderView.cs b/Assets/Scripts/UI/Terminal/TerminalHeaderView.cs index 24a4406be..d3e0d7d61 100644 --- a/Assets/Scripts/UI/Terminal/TerminalHeaderView.cs +++ b/Assets/Scripts/UI/Terminal/TerminalHeaderView.cs @@ -1,24 +1,59 @@ +using System; +using AibisDream.Utility; using TMPro; using UnityEngine; +using UnityEngine.Localization.Components; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalHeaderView : MonoBehaviour { [SerializeField] private TMP_Text titleText; + [SerializeField] private LocalizeStringEvent titleLocalize; [SerializeField] private TMP_Text statusText; + private void Awake() + { + if (titleLocalize == null && titleText != null) + { + titleLocalize = titleText.GetComponent(); + } + } + public void SetText(string title, string status = "") { - if (titleText != null) - { - titleText.text = title; - } + SetTitle(title); if (statusText != null) { statusText.text = status; } } + + public void SetTitle(string title) + { + if (titleLocalize != null && IsLocalizationKey(title)) + { + titleLocalize.enabled = true; + titleLocalize.SetTable(ConstRef.UITextTable); + titleLocalize.SetEntry(title); + return; + } + + if (titleLocalize != null) + { + titleLocalize.enabled = false; + } + + if (titleText != null) + { + titleText.text = title; + } + } + + private static bool IsLocalizationKey(string value) + { + return !string.IsNullOrEmpty(value) && value.Contains("_", StringComparison.Ordinal); + } } } diff --git a/Assets/Scripts/UI/Terminal/TerminalLoadingPanel.cs b/Assets/Scripts/UI/Terminal/TerminalLoadingPanel.cs index 83e4053aa..9fc190f71 100644 --- a/Assets/Scripts/UI/Terminal/TerminalLoadingPanel.cs +++ b/Assets/Scripts/UI/Terminal/TerminalLoadingPanel.cs @@ -1,8 +1,9 @@ +using System; using TMPro; using UnityEngine; using UnityEngine.UI; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalLoadingPanel : MonoBehaviour, IUIPanel { @@ -18,6 +19,32 @@ namespace AibisDream.UI.Terminal public bool IsCloseable => false; public void Show() + { + if (panelAnimator != null) + { + panelAnimator.Show(prepare: PrepareContent); + } + else + { + PrepareContent(); + gameObject.SetActive(true); + } + } + + public void Hide(Action onComplete = null) + { + if (panelAnimator != null) + { + panelAnimator.Hide(onComplete); + } + else + { + gameObject.SetActive(false); + onComplete?.Invoke(); + } + } + + private void PrepareContent() { SetText(companyText, "SATISFYING INC"); SetText(deviceText, "Device Model: Dell P5999 DX130\nDevice Serial Number: SN46456546DAS"); @@ -29,27 +56,6 @@ namespace AibisDream.UI.Terminal { avatarImage.gameObject.SetActive(avatarImage.sprite != null); } - - if (panelAnimator != null) - { - panelAnimator.Show(); - } - else - { - gameObject.SetActive(true); - } - } - - public void Hide() - { - if (panelAnimator != null) - { - panelAnimator.Hide(); - } - else - { - gameObject.SetActive(false); - } } public void SetProgress(float progress) diff --git a/Assets/Scripts/UI/Terminal/TerminalMenuItemView.cs b/Assets/Scripts/UI/Terminal/TerminalMenuItemView.cs index e10d9b9fe..8913d563a 100644 --- a/Assets/Scripts/UI/Terminal/TerminalMenuItemView.cs +++ b/Assets/Scripts/UI/Terminal/TerminalMenuItemView.cs @@ -1,18 +1,22 @@ using System; +using AibisDream.Utility; using TMPro; using UnityEngine; +using UnityEngine.Localization.Components; using UnityEngine.UI; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { [RequireComponent(typeof(BaseButton))] public class TerminalMenuItemView : MonoBehaviour { [SerializeField] private TMP_Text labelText; + [SerializeField] private LocalizeStringEvent labelLocalize; [SerializeField] private TMP_Text statusText; [SerializeField] private TMP_Text detailText; [SerializeField] private TMP_Text cursor; [SerializeField] private Image selectionBar; + [SerializeField] private string defaultLabelKey = string.Empty; [SerializeField] private Color normalTextColor = Color.white; [SerializeField] private Color selectedTextColor = new Color(0.02f, 0.02f, 0.04f); [SerializeField] private Color dangerTextColor = new Color(1f, 0.18f, 0.2f); @@ -35,9 +39,19 @@ namespace AibisDream.UI.Terminal _canvasGroup = gameObject.AddComponent(); } + if (labelLocalize == null && labelText != null) + { + labelLocalize = labelText.GetComponent(); + } + _button.onClick.AddListener(HandleClick); _button.onStateTransition.AddListener(OnButtonStateTransition); SetSelected(false); + + if (!string.IsNullOrEmpty(defaultLabelKey)) + { + SetLabel(defaultLabelKey); + } } private void OnDestroy() @@ -49,13 +63,9 @@ namespace AibisDream.UI.Terminal } } - public void SetContent(string label, string status = "", string detail = "", bool isDanger = false) + public void SetContent(string label, string status = "", string detail = "") { - _isDanger = isDanger; - if (labelText != null) - { - labelText.text = label; - } + SetLabel(label); if (statusText != null) { @@ -70,6 +80,27 @@ namespace AibisDream.UI.Terminal RefreshVisual(); } + public void SetLabel(string value) + { + if (labelLocalize != null && IsLocalizationKey(value)) + { + labelLocalize.enabled = true; + labelLocalize.SetTable(ConstRef.UITextTable); + labelLocalize.SetEntry(value); + return; + } + + if (labelLocalize != null) + { + labelLocalize.enabled = false; + } + + if (labelText != null) + { + labelText.text = value; + } + } + public void SetInteractable(bool interactable) { if (_button != null) @@ -135,5 +166,10 @@ namespace AibisDream.UI.Terminal text.color = color; } } + + private static bool IsLocalizationKey(string value) + { + return !string.IsNullOrEmpty(value) && value.Contains("_", StringComparison.Ordinal); + } } } diff --git a/Assets/Scripts/UI/Terminal/TerminalOptionItemView.cs b/Assets/Scripts/UI/Terminal/TerminalOptionItemView.cs index 679b4b18e..b573af107 100644 --- a/Assets/Scripts/UI/Terminal/TerminalOptionItemView.cs +++ b/Assets/Scripts/UI/Terminal/TerminalOptionItemView.cs @@ -3,7 +3,7 @@ using TMPro; using UnityEngine; using UnityEngine.UI; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalOptionItemView : MonoBehaviour { diff --git a/Assets/Scripts/UI/Terminal/TerminalPanel.cs b/Assets/Scripts/UI/Terminal/TerminalPanel.cs index ed84674f4..0d38cf0f2 100644 --- a/Assets/Scripts/UI/Terminal/TerminalPanel.cs +++ b/Assets/Scripts/UI/Terminal/TerminalPanel.cs @@ -1,8 +1,9 @@ using System; +using System; using System.Collections.Generic; using UnityEngine; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public enum TerminalPage { @@ -10,56 +11,47 @@ namespace AibisDream.UI.Terminal Start, Setting, Chapter, - Record } - public enum TerminalOpenSource - { - None, - MainMenu, - InGame - } - - public class TerminalPanel : MonoBehaviour, IUIPanel + /// + /// 主菜单终端 Panel 壳:管理 Start / Chapter / Setting 三个 Page。 + /// + public class TerminalPanel : MonoBehaviour, IUIPanel, ITerminalPanelHost { [SerializeField] private TerminalHeaderView header; [SerializeField] private TerminalStartPanel startPage; [SerializeField] private TerminalSettingPanel settingPage; [SerializeField] private TerminalChapterPanel chapterPage; - [SerializeField] private TerminalRecordPanel recordPage; private readonly Stack _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 bool IsCloseable => false; public TerminalPage CurrentPage => _currentPage; - public TerminalOpenSource Source => _source; + public bool IsInGameContext => false; - public void Initialize(RectTransform searchRoot) + public void Initialize() { - startPage ??= searchRoot.GetComponentInChildren(true); - settingPage ??= searchRoot.GetComponentInChildren(true); - chapterPage ??= searchRoot.GetComponentInChildren(true); - recordPage ??= searchRoot.GetComponentInChildren(true); + var root = transform; + startPage ??= root.GetComponentInChildren(true); + settingPage ??= root.GetComponentInChildren(true); + chapterPage ??= root.GetComponentInChildren(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; + return startPage != null && settingPage != null && chapterPage != null; } public void Show() { - Open(TerminalPage.Start, TerminalOpenSource.MainMenu); + Open(TerminalPage.Start); } public void Hide() @@ -67,21 +59,19 @@ namespace AibisDream.UI.Terminal Close(); } - public void Open(TerminalPage page, TerminalOpenSource source) + public void Open(TerminalPage page) { - if (!HasRequiredPages()) + if (!HasRequiredPages() || page == TerminalPage.None) { return; } - if (!IsOpen || _source != source) + if (!IsOpen) { _backStack.Clear(); } gameObject.SetActive(true); - _source = source; - PauseIfNeeded(page, source); SwitchTo(page, pushCurrent: false); } @@ -97,25 +87,26 @@ namespace AibisDream.UI.Terminal _backStack.Push(_currentPage); } - HidePage(_currentPage); + var previous = _currentPage; _currentPage = page; - ShowPage(_currentPage); + + if (previous == TerminalPage.None) + { + ShowPage(_currentPage); + return; + } + + HidePage(previous, () => ShowPage(_currentPage)); } public void Back() { - if (_source == TerminalOpenSource.InGame) - { - Close(); - return; - } - if (_backStack.Count > 0) { var previous = _backStack.Pop(); - HidePage(_currentPage); + var current = _currentPage; _currentPage = previous; - ShowPage(_currentPage); + HidePage(current, () => ShowPage(_currentPage)); return; } @@ -152,8 +143,6 @@ namespace AibisDream.UI.Terminal HideAllPages(); _backStack.Clear(); _currentPage = TerminalPage.None; - _source = TerminalOpenSource.None; - ContinueIfPaused(); gameObject.SetActive(false); } @@ -163,46 +152,11 @@ namespace AibisDream.UI.Terminal 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) @@ -216,27 +170,50 @@ namespace AibisDream.UI.Terminal case TerminalPage.Chapter: chapterPage?.Show(); break; - case TerminalPage.Record: - recordPage?.Show(); - break; } } - private void HidePage(TerminalPage page) + private void HidePage(TerminalPage page, Action onComplete = null) { switch (page) { case TerminalPage.Start: - startPage?.Hide(); + HidePage(startPage, onComplete); break; case TerminalPage.Setting: - settingPage?.Hide(); + HidePage(settingPage, onComplete); break; case TerminalPage.Chapter: - chapterPage?.Hide(); + HidePage(chapterPage, onComplete); break; - case TerminalPage.Record: - recordPage?.Hide(); + 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; } } @@ -246,7 +223,6 @@ namespace AibisDream.UI.Terminal startPage?.Hide(); settingPage?.Hide(); chapterPage?.Hide(); - recordPage?.Hide(); } } } diff --git a/Assets/Scripts/UI/Terminal/TerminalPanelAnimator.cs b/Assets/Scripts/UI/Terminal/TerminalPanelAnimator.cs index e55771d96..d6627a165 100644 --- a/Assets/Scripts/UI/Terminal/TerminalPanelAnimator.cs +++ b/Assets/Scripts/UI/Terminal/TerminalPanelAnimator.cs @@ -1,15 +1,20 @@ -using System; +using System; using DG.Tweening; using UnityEngine; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalPanelAnimator : MonoBehaviour { [SerializeField] private CanvasGroup canvasGroup; [SerializeField] private RectTransform sweep; - [SerializeField] private float fadeDuration = 0.18f; - [SerializeField] private float sweepDuration = 0.24f; + + [Header("终端风格")] + [SerializeField, Range(0f, 1f)] private float visibleAlpha = 0.88f; + [SerializeField] private float fadeDuration = 0.06f; + [SerializeField] private Ease showEase = Ease.Linear; + [SerializeField] private Ease hideEase = Ease.Linear; + [SerializeField] private float sweepDuration = 0.08f; private Tween _activeTween; @@ -21,10 +26,23 @@ namespace AibisDream.UI.Terminal } } - public void Show(Action onComplete = null) + /// 淡入完成回调。 + /// 在 SetActive 之前执行的内容准备(赋值、布局等)。 + public void Show(Action onComplete = null, Action prepare = null) { - gameObject.SetActive(true); _activeTween?.Kill(); + prepare?.Invoke(); + + if (canvasGroup != null) + { + canvasGroup.alpha = 0f; + canvasGroup.interactable = false; + canvasGroup.blocksRaycasts = false; + } + + PrepareSweep(); + gameObject.SetActive(true); + Canvas.ForceUpdateCanvases(); if (canvasGroup == null) { @@ -32,16 +50,11 @@ namespace AibisDream.UI.Terminal return; } - canvasGroup.alpha = 0f; - canvasGroup.interactable = false; - canvasGroup.blocksRaycasts = false; - PrepareSweep(); - var sequence = DOTween.Sequence().SetUpdate(true); - sequence.Join(canvasGroup.DOFade(1f, fadeDuration)); + sequence.Join(canvasGroup.DOFade(visibleAlpha, fadeDuration).SetEase(showEase)); if (sweep != null) { - sequence.Join(sweep.DOAnchorPosX(0f, sweepDuration).SetEase(Ease.OutQuad)); + sequence.Join(sweep.DOAnchorPosX(0f, sweepDuration).SetEase(Ease.Linear)); } sequence.OnComplete(() => @@ -69,6 +82,7 @@ namespace AibisDream.UI.Terminal _activeTween = canvasGroup .DOFade(0f, fadeDuration) + .SetEase(hideEase) .SetUpdate(true) .OnComplete(() => { diff --git a/Assets/Scripts/UI/Terminal/TerminalRecordItemView.cs b/Assets/Scripts/UI/Terminal/TerminalRecordItemView.cs index ccdcfd989..11f77ca86 100644 --- a/Assets/Scripts/UI/Terminal/TerminalRecordItemView.cs +++ b/Assets/Scripts/UI/Terminal/TerminalRecordItemView.cs @@ -1,7 +1,7 @@ using TMPro; using UnityEngine; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalRecordItemView : MonoBehaviour { @@ -13,11 +13,13 @@ namespace AibisDream.UI.Terminal if (actorNameText != null) { actorNameText.text = lineInfo.character.GetActorName(); + actorNameText.ForceMeshUpdate(); } if (lineText != null) { lineText.text = lineInfo.GetRecordLine(); + lineText.ForceMeshUpdate(); } gameObject.SetActive(true); diff --git a/Assets/Scripts/UI/Terminal/TerminalRecordPanel.cs b/Assets/Scripts/UI/Terminal/TerminalRecordPanel.cs index ae933d8f4..9ea827ab2 100644 --- a/Assets/Scripts/UI/Terminal/TerminalRecordPanel.cs +++ b/Assets/Scripts/UI/Terminal/TerminalRecordPanel.cs @@ -1,11 +1,15 @@ +using System; using System.Collections.Generic; using AibisDream.Utility; using TMPro; using UnityEngine; using UnityEngine.UI; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { + /// + /// 终端 Record Page(遗留类名 Panel)。挂在局内 InGameTerminalPanel 壳下。 + /// public class TerminalRecordPanel : MonoBehaviour { [SerializeField] private TerminalPanelAnimator panelAnimator; @@ -16,11 +20,11 @@ namespace AibisDream.UI.Terminal [SerializeField] private TMP_Text emptyText; private readonly List _recordDataList = new List(); + private InGameTerminalPanel _owner; public bool IsOpen => gameObject.activeSelf; - private TerminalPanel _owner; - public void SetOwner(TerminalPanel owner) + public void SetOwner(InGameTerminalPanel owner) { _owner = owner; } @@ -28,6 +32,39 @@ namespace AibisDream.UI.Terminal private void Awake() { backButton?.onClick.AddListener(ReturnGame); + EnsureScrollViewportMask(); + } + + private void EnsureScrollViewportMask() + { + if (scrollRect == null) + { + return; + } + + var legacyMask = scrollRect.GetComponent(); + if (legacyMask != null) + { + legacyMask.enabled = false; + } + + if (scrollRect.TryGetComponent(out var scrollImage)) + { + var color = scrollImage.color; + color.a = 0f; + scrollImage.color = color; + scrollImage.raycastTarget = true; + } + + if (scrollRect.viewport == null) + { + return; + } + + if (scrollRect.viewport.GetComponent() == null) + { + scrollRect.viewport.gameObject.AddComponent(); + } } private void OnDestroy() @@ -37,30 +74,34 @@ namespace AibisDream.UI.Terminal public void Show() { - _owner?.SetHeader("<诊疗记录>", "EMR 511/42 - Clinical Record"); - DrawRecords(); + _owner?.SetHeader("termnial_header_record", "REPAIR_STATION_07 - AUTH_VERIFIED"); if (panelAnimator != null) { - panelAnimator.Show(); + panelAnimator.Show(onComplete: () => StartCoroutine(DrawRecordsWhenReady())); } else { gameObject.SetActive(true); + StartCoroutine(DrawRecordsWhenReady()); } } - public void Hide() + public void Hide(Action onComplete = null) { - ClearRecordItems(); if (panelAnimator != null) { - panelAnimator.Hide(); + panelAnimator.Hide(() => + { + ClearRecordItems(); + onComplete?.Invoke(); + }); } else { + ClearRecordItems(); gameObject.SetActive(false); + onComplete?.Invoke(); } - } public void AppendDialogLine(LineInfo lineInfo) @@ -73,10 +114,50 @@ namespace AibisDream.UI.Terminal _recordDataList.Clear(); } + private System.Collections.IEnumerator DrawRecordsWhenReady() + { + yield return null; + DrawRecords(); + } + + private void ConfigureContentRect() + { + if (recordRoot is not RectTransform content) + { + return; + } + + content.anchorMin = new Vector2(0f, 1f); + content.anchorMax = new Vector2(1f, 1f); + content.pivot = new Vector2(0.5f, 1f); + content.anchoredPosition = Vector2.zero; + content.sizeDelta = Vector2.zero; + } + + private static void ConfigureRecordItemRect(RectTransform itemRect) + { + itemRect.anchorMin = new Vector2(0f, 1f); + itemRect.anchorMax = new Vector2(1f, 1f); + itemRect.pivot = new Vector2(0.5f, 1f); + itemRect.sizeDelta = Vector2.zero; + } + + private static void SyncUiLayer(GameObject target, int layer) + { + target.layer = layer; + for (var i = 0; i < target.transform.childCount; i++) + { + SyncUiLayer(target.transform.GetChild(i).gameObject, layer); + } + } + private void DrawRecords() { ClearRecordItems(); + ConfigureContentRect(); + var hasRecords = _recordDataList.Count > 0; + if (emptyText != null) { emptyText.gameObject.SetActive(!hasRecords); @@ -85,12 +166,24 @@ namespace AibisDream.UI.Terminal foreach (var recordData in _recordDataList) { var item = Instantiate(recordItemPrefab, recordRoot); + SyncUiLayer(item.gameObject, recordRoot.gameObject.layer); item.Init(recordData); + + if (item.transform is RectTransform itemRect) + { + ConfigureRecordItemRect(itemRect); + } + } + + if (recordRoot is RectTransform contentRect) + { + LayoutRebuilder.ForceRebuildLayoutImmediate(contentRect); } if (scrollRect != null) { - scrollRect.verticalNormalizedPosition = 0f; + Canvas.ForceUpdateCanvases(); + scrollRect.verticalNormalizedPosition = 1f; } } @@ -104,7 +197,7 @@ namespace AibisDream.UI.Terminal private void ReturnGame() { - _owner.Back(); + _owner?.Back(); } } } diff --git a/Assets/Scripts/UI/Terminal/TerminalSettingPanel.cs b/Assets/Scripts/UI/Terminal/TerminalSettingPanel.cs index d42911f5f..c7f59fd47 100644 --- a/Assets/Scripts/UI/Terminal/TerminalSettingPanel.cs +++ b/Assets/Scripts/UI/Terminal/TerminalSettingPanel.cs @@ -1,8 +1,12 @@ -using UnityEngine; -using UnityEngine.UI; +using System; +using AibisDream; +using UnityEngine; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { + /// + /// 终端 Setting Page(遗留类名 Panel)。可挂在主菜单或局内 Panel 壳下。 + /// public class TerminalSettingPanel : MonoBehaviour { [SerializeField] private TerminalPanelAnimator panelAnimator; @@ -10,12 +14,15 @@ namespace AibisDream.UI.Terminal [SerializeField] private SelectItem textSpeedOption; [SerializeField] private SelectItem windowModeOption; [SerializeField] private SelectItem languageOption; - [SerializeField] private Button continueButton; - [SerializeField] private Button backToMainButton; - [SerializeField] private Button quitButton; + [SerializeField] private TerminalButton continueButton; + [SerializeField] private TerminalButton backToMainButton; + [SerializeField] private TerminalButton quitButton; public bool IsOpen => gameObject.activeSelf; - private TerminalPanel _owner; + private ITerminalPanelHost _owner; + private RectTransform _continueButtonRect; + private Vector2 _continueButtonDefaultPosition; + private bool _hasContinueButtonDefaultPosition; private static readonly UIFormOption[] WindowModeOptions = { @@ -37,59 +44,85 @@ namespace AibisDream.UI.Terminal new UIFormOption { prop = "3.0", label = "setting_textSpeed_fast" }, }; - public void SetOwner(TerminalPanel owner) + public void SetOwner(ITerminalPanelHost owner) { _owner = owner; } private void Awake() { - continueButton?.onClick.AddListener(BackToGame); - backToMainButton?.onClick.AddListener(BackToMain); - quitButton?.onClick.AddListener(QuitApp); + BindButton(continueButton, BackToGame); + BindButton(backToMainButton, BackToMain); + BindButton(quitButton, QuitApp); + + if (continueButton != null) + { + _continueButtonRect = continueButton.transform as RectTransform; + if (_continueButtonRect != null) + { + _continueButtonDefaultPosition = _continueButtonRect.anchoredPosition; + _hasContinueButtonDefaultPosition = true; + } + } } private void OnDestroy() { - continueButton?.onClick.RemoveListener(BackToGame); - backToMainButton?.onClick.RemoveListener(BackToMain); - quitButton?.onClick.RemoveListener(QuitApp); + UnbindButton(continueButton, BackToGame); + UnbindButton(backToMainButton, BackToMain); + UnbindButton(quitButton, QuitApp); + } + + private static void BindButton(TerminalButton terminalButton, UnityEngine.Events.UnityAction action) + { + terminalButton?.Button?.onClick.AddListener(action); + } + + private static void UnbindButton(TerminalButton terminalButton, UnityEngine.Events.UnityAction action) + { + terminalButton?.Button?.onClick.RemoveListener(action); } public void Show() { - _owner?.SetHeader("<设置>", "MUSIC 50/100 - AUDIO INPUT 001"); - InitControls(); - RefreshControls(); - if (panelAnimator != null) { - panelAnimator.Show(); + panelAnimator.Show(prepare: PrepareContent); } else { + PrepareContent(); gameObject.SetActive(true); } } - public void Hide() + public void Hide(Action onComplete = null) { if (panelAnimator != null) { - panelAnimator.Hide(); + panelAnimator.Hide(onComplete); } else { gameObject.SetActive(false); + onComplete?.Invoke(); } } + private void PrepareContent() + { + _owner?.SetHeader("terminal_header_setting", "REPAIR_STATION_07 - AUTH_VERIFIED"); + InitControls(); + RefreshControls(); + RefreshFooterButtons(); + } + private void InitControls() { - volumeSlider?.Init("Volume", "音量.....", 0f, 1f, OnSettingChanged); - textSpeedOption?.Init("TextSpeed", "文字速度", TextSpeedOptions, OnSettingChanged); - windowModeOption?.Init("WindowMode", "屏幕模式", WindowModeOptions, OnSettingChanged); - languageOption?.Init("Language", "语言.....", LanguageOptions, OnSettingChanged); + volumeSlider?.Init("Volume", "setting_volume", 0f, 1f, OnSettingChanged); + textSpeedOption?.Init("TextSpeed", "setting_textSpeed", TextSpeedOptions, OnSettingChanged); + windowModeOption?.Init("WindowMode", "setting_window", WindowModeOptions, OnSettingChanged); + languageOption?.Init("Language", "setting_language", LanguageOptions, OnSettingChanged); } private void RefreshControls() @@ -121,9 +154,37 @@ namespace AibisDream.UI.Terminal SettingLoader.Instance.Write(propName, value); } + private void RefreshFooterButtons() + { + var inGame = _owner != null && _owner.IsInGameContext; + + continueButton?.gameObject.SetActive(true); + backToMainButton?.gameObject.SetActive(inGame); + quitButton?.gameObject.SetActive(inGame); + + ApplyContinueButtonLayout(inGame); + } + + private void ApplyContinueButtonLayout(bool inGame) + { + if (!_hasContinueButtonDefaultPosition || _continueButtonRect == null) + { + return; + } + + _continueButtonRect.anchoredPosition = inGame + ? _continueButtonDefaultPosition + : new Vector2(0f, _continueButtonDefaultPosition.y); + } + private void BackToGame() { - if (_owner.Source == TerminalOpenSource.InGame) + if (_owner == null) + { + return; + } + + if (_owner.IsInGameContext) { _owner.Close(); } @@ -135,7 +196,7 @@ namespace AibisDream.UI.Terminal private void BackToMain() { - _owner.CloseToMainMenu(); + _owner?.CloseToMainMenu(); } private static void QuitApp() diff --git a/Assets/Scripts/UI/Terminal/TerminalSliderView.cs b/Assets/Scripts/UI/Terminal/TerminalSliderView.cs index 17bf37106..24f2955b9 100644 --- a/Assets/Scripts/UI/Terminal/TerminalSliderView.cs +++ b/Assets/Scripts/UI/Terminal/TerminalSliderView.cs @@ -4,7 +4,7 @@ using TMPro; using UnityEngine; using UnityEngine.UI; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalSliderView : MonoBehaviour { diff --git a/Assets/Scripts/UI/Terminal/TerminalStartPanel.cs b/Assets/Scripts/UI/Terminal/TerminalStartPanel.cs index c5579fb03..b5fe36440 100644 --- a/Assets/Scripts/UI/Terminal/TerminalStartPanel.cs +++ b/Assets/Scripts/UI/Terminal/TerminalStartPanel.cs @@ -1,7 +1,7 @@ using System; using UnityEngine; -namespace AibisDream.UI.Terminal +namespace AibisDream.UI { public class TerminalStartPanel : MonoBehaviour { @@ -23,6 +23,7 @@ namespace AibisDream.UI.Terminal private void Awake() { + InitMenuItems(); RegisterItem(newGameItem, StartNewGame); RegisterItem(chapterItem, OpenChapter); RegisterItem(settingItem, OpenSetting); @@ -39,29 +40,38 @@ namespace AibisDream.UI.Terminal public void Show() { - _owner?.SetHeader("<选择章节>", "CHAPER xx/xxx - LOADING - DT"); if (panelAnimator != null) { - panelAnimator.Show(); + panelAnimator.Show(prepare: () => _owner?.SetHeader("termnial_header_start", "REPAIR_STATION_07 - AUTH_VERIFIED")); } else { + _owner?.SetHeader("termnial_header_start", "REPAIR_STATION_07 - AUTH_VERIFIED"); gameObject.SetActive(true); } } - public void Hide() + public void Hide(Action onComplete = null) { if (panelAnimator != null) { - panelAnimator.Hide(); + panelAnimator.Hide(onComplete); } else { gameObject.SetActive(false); + onComplete?.Invoke(); } } + private void InitMenuItems() + { + newGameItem?.SetContent("start_newGame", "SESSION CREATE", "STATUS: ACTIVE"); + chapterItem?.SetContent("start_selcetLevel", "HISTORY QUERY", "RANGE: 04/533"); + settingItem?.SetContent("start_setting", "USER CONFIG", "AUTH: LOCAL"); + quitItem?.SetContent("start_quit", "SESSION CLOSE", "LOG: SAVED"); + } + private void RegisterItem(TerminalMenuItemView item, Action action) { if (item == null) diff --git a/Assets/Scripts/UI/UIManager.cs b/Assets/Scripts/UI/UIManager.cs index be8e4372a..c10de63db 100644 --- a/Assets/Scripts/UI/UIManager.cs +++ b/Assets/Scripts/UI/UIManager.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using AibisDream.Framework; using AibisDream.UI; -using AibisDream.UI.Terminal; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; @@ -25,6 +24,7 @@ namespace AibisDream private GraphicRaycaster _raycaster; private TerminalPanel _terminalPanel; + private InGameTerminalPanel _inGameTerminalPanel; #endregion @@ -37,6 +37,11 @@ namespace AibisDream { if (Input.GetKeyDown(KeyCode.Escape)) { + if (_inGameTerminalPanel != null && _inGameTerminalPanel.HandleEscape()) + { + return; + } + if (_terminalPanel != null && _terminalPanel.HandleEscape()) { return; @@ -78,6 +83,7 @@ namespace AibisDream } RegisterTerminalPanel(); + RegisterInGameTerminalPanel(); EnumEventSystem.Global.Register(GameLoopEnum.AppStart, OnAppStart); EnumEventSystem.Global.Register(GameLoopEnum.GameStart, OnGameStart); @@ -101,7 +107,7 @@ namespace AibisDream return; } - _terminalPanel.Initialize(Canvas); + _terminalPanel.Initialize(); if (_terminalPanel.HasRequiredPages()) { _panelPool[typeof(TerminalPanel)] = _terminalPanel; @@ -112,6 +118,35 @@ namespace AibisDream } } + private void RegisterInGameTerminalPanel() + { + if (_panelPool.TryGetValue(typeof(InGameTerminalPanel), out var registeredPanel)) + { + _inGameTerminalPanel = registeredPanel as InGameTerminalPanel; + } + else + { + _inGameTerminalPanel = Canvas.GetComponentInChildren(true); + } + + if (_inGameTerminalPanel == null) + { + Debug.LogError("[UIManager] InGameTerminalPanel not found under UI Canvas. Add the in-game terminal shell and assign its Setting/Record prefab pages in the scene."); + return; + } + + _inGameTerminalPanel.Initialize(); + var hasPages = _inGameTerminalPanel.HasRequiredPages(); + if (hasPages) + { + _panelPool[typeof(InGameTerminalPanel)] = _inGameTerminalPanel; + } + else + { + Debug.LogError("[UIManager] InGameTerminalPanel is missing required pages. Assign TerminalSettingPanel and TerminalRecordPanel on the in-game shell."); + } + } + private void OnAppStart() { // 游戏打开 @@ -121,6 +156,7 @@ namespace AibisDream HidePanel(); HideTerminalSetting(); HideTerminalRecord(); + _inGameTerminalPanel?.Close(); HidePanel(); } @@ -128,6 +164,7 @@ namespace AibisDream { // 开始游戏 HideTerminal(); + _inGameTerminalPanel?.Close(); ShowPanel(); } @@ -213,7 +250,7 @@ namespace AibisDream public void ShowTerminalStart() { - GetPanel()?.Open(TerminalPage.Start, TerminalOpenSource.MainMenu); + GetPanel()?.Open(TerminalPage.Start); } public void HideTerminal() @@ -223,25 +260,26 @@ namespace AibisDream public void ShowTerminalSetting() { - var source = GameManager.Instance != null && GameManager.Instance.state.isInGame - ? TerminalOpenSource.InGame - : TerminalOpenSource.MainMenu; - GetPanel()?.Open(TerminalPage.Setting, source); + if (GameManager.Instance != null && GameManager.Instance.state.isInGame) + { + GetPanel()?.Open(InGameTerminalPage.Setting); + return; + } + + GetPanel()?.Open(TerminalPage.Setting); } public void HideTerminalSetting() { - var terminal = GetPanel(); - if (terminal == null || !terminal.IsOpen || terminal.CurrentPage != TerminalPage.Setting) + var inGame = GetPanel(); + if (inGame != null && inGame.IsOpen && inGame.CurrentPage == InGameTerminalPage.Setting) { + inGame.Close(); return; } - if (terminal.Source == TerminalOpenSource.InGame) - { - terminal.Close(); - } - else + var terminal = GetPanel(); + if (terminal != null && terminal.IsOpen && terminal.CurrentPage == TerminalPage.Setting) { terminal.Back(); } @@ -249,7 +287,7 @@ namespace AibisDream public void ShowTerminalChapter() { - GetPanel()?.Open(TerminalPage.Chapter, TerminalOpenSource.MainMenu); + GetPanel()?.Open(TerminalPage.Chapter); } public void HideTerminalChapter() @@ -263,15 +301,16 @@ namespace AibisDream public void ShowTerminalRecord() { - GetPanel()?.Open(TerminalPage.Record, TerminalOpenSource.InGame); + var panel = GetPanel(); + panel?.Open(InGameTerminalPage.Record); } public void HideTerminalRecord() { - var terminal = GetPanel(); - if (terminal != null && terminal.IsOpen && terminal.CurrentPage == TerminalPage.Record) + var inGame = GetPanel(); + if (inGame != null && inGame.IsOpen && inGame.CurrentPage == InGameTerminalPage.Record) { - terminal.Back(); + inGame.Close(); } }