创建开始菜单面板和设置面板

- 添加 MenuForm 与 SettingForm
- 为带左右按钮的水平选择界面添加HorizonSelectGroup预制件。
- 添加游戏核心可配置项
This commit is contained in:
SepComet
2026-02-10 15:10:31 +08:00
parent 7ac832f74b
commit 13583354f9
111 changed files with 11247 additions and 755 deletions
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 8cb383e8977814343a630c164eed6c51
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ea2618629fd6bbd40a9dda07b40a0094
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
namespace UI
{
public class MenuFormContext : UIContext
{
public bool HasGameData = false;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8cdd22c948028246b9d1d7afd80f08c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using Definition.DataStruct;
using UnityEngine;
namespace UI
{
public class SettingFormContext : UIContext
{
public SettingFormController Controller;
public GameSetting Setting;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c2754dbcd65873f4b9d3f579c12cef58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,132 @@
using GameFramework.Event;
using UnityGameFramework.Runtime;
namespace UI
{
public class MenuFormController : IFormController<MenuFormContext>
{
private MenuFormContext _context;
private MenuForm _menuForm;
private int? _menuFormSerialId;
private bool _pendingRefresh;
public MenuFormController()
{
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
}
public int? OpenUI(MenuFormContext context)
{
if (context == null)
{
Log.Warning("MenuFormController open failed. context is null.");
return null;
}
_context = context;
if (_menuForm != null)
{
_menuForm.RefreshUI(_context);
return _menuFormSerialId;
}
CloseUI();
_pendingRefresh = true;
_menuFormSerialId = GameEntry.UI.OpenUIForm(UIFormId.MenuForm, context);
return _menuFormSerialId;
}
public void CloseUI()
{
_pendingRefresh = false;
if (_menuFormSerialId.HasValue)
{
GameEntry.UI.CloseUIForm(_menuFormSerialId.Value);
return;
}
if (_menuForm != null)
{
_menuForm.Close();
}
}
private void TryRefreshUI()
{
if (_context == null)
{
return;
}
if (_menuForm == null)
{
_pendingRefresh = true;
return;
}
_menuForm.RefreshUI(_context);
_pendingRefresh = false;
}
~MenuFormController()
{
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, CloseUIFormComplete);
}
#region EventHanlders
private void OpenUIFormSuccess(object sender, GameEventArgs e)
{
if (!(e is OpenUIFormSuccessEventArgs args))
{
return;
}
if (!_menuFormSerialId.HasValue)
{
return;
}
if (args.UIForm == null || args.UIForm.SerialId != _menuFormSerialId.Value || args.UserData != _context)
{
return;
}
_menuForm = args.UIForm.Logic as MenuForm;
if (_menuForm == null)
{
Log.Warning("DialogFormController open success but form logic is invalid.");
return;
}
if (_pendingRefresh)
{
TryRefreshUI();
}
}
private void CloseUIFormComplete(object sender, GameEventArgs e)
{
if (!(e is CloseUIFormCompleteEventArgs args))
{
return;
}
if (args.SerialId != _menuFormSerialId)
{
return;
}
_menuForm = null;
_menuFormSerialId = null;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38fe84430afbe554f8f8c81e3f6ac47b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,129 @@
using System;
using GameFramework.Event;
using UnityGameFramework.Runtime;
namespace UI
{
public class SettingFormController : IFormController<SettingFormContext>
{
private SettingFormContext _context;
private SettingForm _settingForm;
private int? _formSerialId;
private bool _pendingRefresh;
public SettingFormController()
{
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
}
public int? OpenUI(SettingFormContext context)
{
if (context == null)
{
Log.Warning("SettingFormController open failed. context is null.");
return null;
}
_context = context;
if (_settingForm != null)
{
_settingForm.RefreshUI(context);
return _formSerialId;
}
CloseUI();
_pendingRefresh = true;
_formSerialId = GameEntry.UI.OpenUIForm(UIFormId.SettingForm, context);
return _formSerialId;
}
public void CloseUI()
{
_pendingRefresh = false;
if (_formSerialId.HasValue)
{
GameEntry.UI.CloseUIForm(_formSerialId.Value);
return;
}
if (_settingForm != null)
{
_settingForm.Close();
}
}
~SettingFormController()
{
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
}
private void TryRefreshUI()
{
if (_context == null)
{
return;
}
if (_settingForm == null)
{
_pendingRefresh = true;
return;
}
_settingForm.RefreshUI(_context);
_pendingRefresh = false;
}
private void OnOpenUIFormSuccess(object sender, GameEventArgs e)
{
if (!(e is OpenUIFormSuccessEventArgs args))
{
return;
}
if (!_formSerialId.HasValue)
{
return;
}
if (args.UIForm == null || args.UIForm.SerialId != _formSerialId.Value || args.UserData != _context)
{
return;
}
_settingForm = args.UIForm.Logic as SettingForm;
if (_settingForm == null)
{
Log.Warning("SettingFormController open success but form logic is invalid.");
return;
}
if (_pendingRefresh)
{
TryRefreshUI();
}
}
private void OnCloseUIFormComplete(object sender, GameEventArgs e)
{
if (!(e is CloseUIFormCompleteEventArgs args))
{
return;
}
if (args.SerialId != _formSerialId)
{
return;
}
_settingForm = null;
_formSerialId = null;
_pendingRefresh = false;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe89c33ff996c344a911bcee3293f439
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d81d2a322c2e93948b4d5aa7e36c99f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ae5528a06773f5a45b75a99f619c2e22
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3c721a41db518484e9a1771952725489
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c4b7102b2e334264398178de3d6325b4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+5 -4
View File
@@ -16,11 +16,14 @@ namespace UI
{
public class DialogForm : UGuiForm
{
[SerializeField] private TMP_Text _titleText = null;
[SerializeField] private TMP_Text _messageText = null;
[SerializeField] private GameObject[] _modeObjects = null;
[SerializeField] private TMP_Text[] _confirmTexts = null;
[SerializeField] private TMP_Text[] _cancelTexts = null;
[SerializeField] private TMP_Text[] _otherTexts = null;
private int _dialogMode = 1;
@@ -83,8 +86,7 @@ namespace UI
_dialogMode = dialogParams.Mode;
RefreshDialogMode();
_titleText.text = dialogParams.Title;
_messageText.text = dialogParams.Message;
_pauseGame = dialogParams.PauseGame;
@@ -114,7 +116,6 @@ namespace UI
}
_dialogMode = 1;
_titleText.text = string.Empty;
_messageText.text = string.Empty;
_pauseGame = false;
_userData = null;
@@ -45,7 +45,7 @@ namespace UI
_context = context;
string speakerName = context.SpeakerName;
string speakerName = _context.SpeakerName;
if (_speakerArea != null)
{
@@ -57,7 +57,7 @@ namespace UI
_speakerNameText.text = speakerName;
}
PlayTypewriter(_contentText, context.Text, context.PlayingSpeed);
PlayTypewriter(_contentText, _context.Text, _context.PlayingSpeed);
if (string.IsNullOrEmpty(speakerName))
{
@@ -0,0 +1,82 @@
using System;
using TMPro;
using UnityEngine;
namespace UI
{
public class HorizonSelectGroup : MonoBehaviour
{
[SerializeField] private TMP_Text _showText;
[SerializeField] private GameObject _leftButton;
[SerializeField] private GameObject _rightButton;
[SerializeField] private int _currentValue;
[SerializeField] private string[] _showTexts;
private int LeftBound = 0;
private int RightBound => _showTexts.Length - 1;
private void Start()
{
SetValue(_currentValue);
_leftButton.SetActive(false);
_rightButton.SetActive(false);
}
public void OnLeftClick()
{
_currentValue--;
_showText.text = _showTexts[_currentValue];
UpdateButtonState();
}
public void OnRightClick()
{
_currentValue++;
_showText.text = _showTexts[_currentValue];
UpdateButtonState();
}
public void SetValue(int value)
{
if (value < 0 || value >= _showTexts.Length)
{
throw new IndexOutOfRangeException();
}
_currentValue = value;
_showText.text = _showTexts[_currentValue];
UpdateButtonState();
}
public void SetValue(bool value)
{
_currentValue = value ? 1 : 0;
_showText.text = _showTexts[_currentValue];
UpdateButtonState();
}
public void UpdateButtonState()
{
_leftButton.SetActive(_currentValue != LeftBound);
_rightButton.SetActive(_currentValue != RightBound);
}
public void HideButtons()
{
_leftButton.SetActive(false);
_rightButton.SetActive(false);
}
public int GetIntValue() => _currentValue;
public bool GetBoolValue() => _currentValue == 1;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3ad474ed1648574f984d626f3d9726a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,49 @@
using Event;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace UI
{
public class MenuForm : UGuiForm
{
[SerializeField] private GameObject _continueButton;
public void RefreshUI(MenuFormContext context)
{
_continueButton.SetActive(context.HasGameData);
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
if (!(userData is MenuFormContext context))
{
Log.Error("MenuFormContext is invalid.");
return;
}
RefreshUI(context);
}
public void OnContinueButtonClick()
{
GameEntry.Event.Fire(this, MenuContinueEventArgs.Create());
}
public void OnStartButtonClick()
{
GameEntry.Event.Fire(this, MenuStartEventArgs.Create());
}
public void OnSettingButtonClick()
{
GameEntry.Event.Fire(this, MenuSettingEventArgs.Create());
}
public void OnExitButtonClick()
{
GameEntry.Event.Fire(this, MenuExitEventArgs.Create());
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 81cd82e6a7856b148bcffd790e5e5ed8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,37 @@
using DG.Tweening;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace UI
{
public class SelectableItem : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
[SerializeField] private Image _bgImage;
[SerializeField] private float _fadeDuration;
private Sequence _fadeSequence;
public UnityEvent _onSelect;
public UnityEvent _onDeselect;
public void OnPointerEnter(PointerEventData eventData)
{
_fadeSequence.Kill();
_fadeSequence = DOTween.Sequence();
_fadeSequence.Append(_bgImage.DOFade(1, _fadeDuration));
_onSelect.Invoke();
}
public void OnPointerExit(PointerEventData eventData)
{
_fadeSequence.Kill();
_fadeSequence = DOTween.Sequence();
_fadeSequence.Append(_bgImage.DOFade(0, _fadeDuration));
_onDeselect.Invoke();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 74ad2e39e0d4fc546ab11cf446143f44
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,134 @@
using Definition.DataStruct;
using Definition.Enum;
using Event;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace UI
{
public class SettingForm : UGuiForm
{
[SerializeField] private Image[] _navigateButtonImages;
[SerializeField] private TMP_Text[] _navigateButtonTexts;
[SerializeField] private Color _darkColor;
[SerializeField] private Color _brightColor;
[SerializeField] private Slider _bgmVolumeSlider;
[SerializeField] private Slider _seVolumeSlider;
[SerializeField] private HorizonSelectGroup _allowShakeGroup;
[SerializeField] private HorizonSelectGroup _allowBlinkGroup;
[SerializeField] private HorizonSelectGroup _dialogWindowAlpha;
[SerializeField] private HorizonSelectGroup _playingSpeed;
[SerializeField] private HorizonSelectGroup _screenSolution;
[SerializeField] private HorizonSelectGroup _screenWindow;
[SerializeField] private HorizonSelectGroup _vSyncGroup;
[SerializeField] private HorizonSelectGroup _antiAliasingGroup;
private SettingFormController _controller;
public void RefreshUI(SettingFormContext context)
{
_controller = context.Controller;
var setting = context.Setting;
_bgmVolumeSlider.value = setting.BGMVolume;
_seVolumeSlider.value = setting.SEVolume;
_allowBlinkGroup.SetValue(setting.AllowBlink);
_allowShakeGroup.SetValue(setting.AllowShake);
_dialogWindowAlpha.SetValue((int)setting.DialogWindowAlpha);
_playingSpeed.SetValue((int)setting.PlayingSpeed);
_screenSolution.SetValue((int)setting.ScreenSolution);
_screenWindow.SetValue((int)setting.ScreenWindow);
_vSyncGroup.SetValue(setting.VSync);
_antiAliasingGroup.SetValue(setting.AntiAliasing);
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
if (!(userData is SettingFormContext context))
{
Log.Error("SettingFormContext is invalid.");
return;
}
RefreshUI(context);
}
private GameSetting CollectSetting()
{
var setting = new GameSetting
{
BGMVolume = _bgmVolumeSlider.value,
SEVolume = _seVolumeSlider.value,
AllowShake = _allowShakeGroup.GetBoolValue(),
AllowBlink = _allowBlinkGroup.GetBoolValue(),
DialogWindowAlpha = (DialogWindowAlpha)_dialogWindowAlpha.GetIntValue(),
PlayingSpeed = (DialogPlayingSpeed)_playingSpeed.GetIntValue(),
ScreenSolution = (ScreenSolutionType)_screenSolution.GetIntValue(),
ScreenWindow = (ScreenWindowType)_screenWindow.GetIntValue(),
VSync = _vSyncGroup.GetBoolValue(),
AntiAliasing = _antiAliasingGroup.GetBoolValue()
};
return setting;
}
public void OnNavigateButtonClick(int index)
{
if (index < 0 || index >= _navigateButtonImages.Length)
{
Log.Error("NavigateButtonClick index is out of range.");
}
for (int i = 0; i < _navigateButtonImages.Length; i++)
{
_navigateButtonImages[i].color = i != index ? _brightColor : _darkColor;
_navigateButtonTexts[i].color = i != index ? _darkColor : _brightColor;
}
}
public void OnReturnButtonClick()
{
var dialogParams = new DialogParams
{
Title = "确认是否返回",
Message = "是否保存更改?",
ConfirmText = "保存并返回",
CancelText = "直接返回",
OtherText = "取消",
Mode = 3,
};
dialogParams.OnClickConfirm += SaveSettingAndReturn;
dialogParams.OnClickCancel += _ => _controller.CloseUI();
GameEntry.UI.OpenUIForm(UIFormId.DialogForm, dialogParams);
}
private void SaveSettingAndReturn(object userData)
{
var setting = CollectSetting();
GameEntry.Event.Fire(this, SettingSaveEventArgs.Create(setting));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbbc5660f5b30714183f047355830159
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: