接入菜单导航并改造通用按钮交互

- 将 CommonButton 改为基于 Unity Button 实现,保留原有 pointer enter、click、pointer exit 三类事件配置,同时接入 Selectable/Button 的导航、选中和提交能力。补充 CommonButton 自定义 Inspector,保留项目自定义事件与背景配置的编辑体验。

- 同步调整菜单相关 UI,将 StartMenuForm 迁移为 MenuForm,并更新 UIForm 配置、菜单 Controller/Form 以及相关 prefab 引用。更新多个 UI prefab 和通用按钮 prefab,使菜单、商店、角色选择、道具展示等界面能够使用新的按钮选中与导航交互。
This commit is contained in:
SepComet
2026-06-12 17:23:34 +08:00
parent 024ec7be99
commit dec18418f5
18 changed files with 1022 additions and 245 deletions
@@ -18,11 +18,11 @@ namespace SepCore.Definition
/// 弹出框。
/// </summary>
DialogForm = 1,
/// <summary>
/// 主菜单。
/// </summary>
MenuForm = 200,
MenuForm = 100,
/// <summary>
/// 设置。
@@ -0,0 +1,39 @@
using SepCore.UI;
using UnityEditor;
using UnityEditor.UI;
namespace SepCore.Editor
{
[CustomEditor(typeof(CommonButton), true)]
[CanEditMultipleObjects]
public class CommonButtonEditor : ButtonEditor
{
private SerializedProperty _background;
private SerializedProperty _onPointerEnterAction;
private SerializedProperty _onClickAction;
private SerializedProperty _onPointerExitAction;
protected override void OnEnable()
{
base.OnEnable();
_background = serializedObject.FindProperty("_background");
_onPointerEnterAction = serializedObject.FindProperty("_onPointerEnterAction");
_onClickAction = serializedObject.FindProperty("_onClickAction");
_onPointerExitAction = serializedObject.FindProperty("_onPointerExitAction");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Common Button", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(_background);
EditorGUILayout.PropertyField(_onPointerEnterAction);
EditorGUILayout.PropertyField(_onClickAction);
EditorGUILayout.PropertyField(_onPointerExitAction);
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b99c0bea7d65bb4eb60742bf62120df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -55,10 +55,10 @@ namespace SepCore.UI
_content.anchoredPosition3D = _targetPos;
}
if (_itemTitle != null) _itemTitle.text = _context.Title ?? string.Empty;
if (_itemTypeText != null) _itemTypeText.text = BuildTypeText(_context.ItemType) ?? string.Empty;
if (_itemDescription != null) _itemDescription.text = _context.Description ?? string.Empty;
if (_itemPrice != null) _itemPrice.text = $"{_context.Price} <sprite name=\"coin\" index=0>";
_itemTitle.text = _context.Title ?? string.Empty;
_itemTypeText.text = BuildTypeText(_context.ItemType) ?? string.Empty;
_itemDescription.text = _context.Description ?? string.Empty;
_itemPrice.text = $"{_context.Price} <sprite name=\"coin\" index=0>";
if (_recycleButton != null)
{
@@ -53,7 +53,7 @@ namespace SepCore.UI
public override void BindUseCase(IUIUseCase useCase)
{
Log.Info("StartMenuForm doesn't need UseCase");
Log.Info("MenuForm doesn't need UseCase");
}
private void OnMenuStartGameButtonClick(object sender, GameEventArgs e)
@@ -1,12 +1,20 @@
using SepCore.Event;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityGameFramework.Runtime;
namespace SepCore.UI
{
public class MenuForm : UGuiForm
{
[SerializeField] private GameObject _defaultButton;
public void RefreshUI(MenuContext context)
{
if (EventSystem.current.currentSelectedGameObject != _defaultButton)
{
EventSystem.current.SetSelectedGameObject(this._defaultButton);
}
}
#region Lifecycle
@@ -21,7 +29,7 @@ namespace SepCore.UI
return;
}
Log.Warning("StartMenuForm requires StartMenuFormContext as userData.");
Log.Warning("MenuForm requires MenuFormContext as userData.");
}
protected override void OnClose(bool isShutdown, object userData)
@@ -1,15 +1,15 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace SepCore.UI
{
public class CommonButton : MonoBehaviour, IPointerEnterHandler, IPointerDownHandler, IPointerExitHandler,
IPointerUpHandler
public class CommonButton : Button
{
private const float FadeTime = 0.3f;
private const float OnHoverAlpha = 0.7f;
private const float OnClickAlpha = 0.6f;
[SerializeField] private Image _background = null;
private Color _originalBackgroundColor;
[SerializeField] private UnityEvent _onPointerEnterAction = new();
@@ -17,64 +17,73 @@ namespace SepCore.UI
[SerializeField] private UnityEvent _onPointerExitAction = new();
[SerializeField] private bool _enableFade = true;
private CanvasGroup _canvasGroup = null;
private void Awake()
protected override void Awake()
{
_canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();
base.Awake();
_originalBackgroundColor = _background.color;
}
private void OnDisable()
protected override void OnEnable()
{
_canvasGroup.alpha = 1f;
_background.color = _originalBackgroundColor;
}
public void OnPointerEnter(PointerEventData eventData)
protected override void OnDisable()
{
_background.color = new Color(0.3f, 0.3f, 0.3f, 0.3f);
}
public override void OnPointerEnter(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
StopAllCoroutines();
if (_enableFade)
StartCoroutine(_canvasGroup.FadeToAlpha(OnHoverAlpha, FadeTime));
EnterHoverState();
}
public override void OnPointerDown(PointerEventData eventData)
{
base.OnPointerDown(eventData);
_onClickAction?.Invoke();
}
public override void OnPointerExit(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
ExitHoverState();
}
public override void OnSelect(BaseEventData eventData)
{
_background.color = new Color(1f, 0.9f, 0.3f);
EnterHoverState();
}
public override void OnDeselect(BaseEventData eventData)
{
_background.color = _originalBackgroundColor;
ExitHoverState();
}
private void EnterHoverState()
{
if (EventSystem.current.currentSelectedGameObject != gameObject)
{
EventSystem.current.SetSelectedGameObject(this.gameObject);
}
_onPointerEnterAction.Invoke();
}
public void OnPointerDown(PointerEventData eventData)
private void ExitHoverState()
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
_canvasGroup.alpha = OnClickAlpha;
_onClickAction.Invoke();
}
public void OnPointerUp(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
_canvasGroup.alpha = OnHoverAlpha;
}
public void OnPointerExit(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
StopAllCoroutines();
if (_enableFade)
StartCoroutine(_canvasGroup.FadeToAlpha(1.0f, FadeTime));
_onPointerExitAction.Invoke();
}
}