Files
aibis-dream/Assets/Scripts/UI/Cursor/CursorManager.cs
T
2025-06-09 23:40:22 +08:00

326 lines
9.4 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AibisDream.Framework;
using AibisDream.Kit;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace AibisDream
{
public class CursorManager : Singleton<CursorManager>
{
private const float DelayTime = 0.1f;
[Header("指针图片")] public Sprite defaultCursor;
public Sprite nextTalk, talking, option, interactive, holding, disable;
private Image _cursor;
private GraphicRaycaster _raycaster;
private Sprite _curCursor;
private CursorState _stateCache;
#region 状态相关
private Coroutine _switchCoroutine;
private CursorState _curState = CursorState.Default;
private bool _isTalking;
private bool _isInGame;
#endregion
// 卸载时注销事件
private readonly List<IUnRegister> _unRegisters = new();
private void Start()
{
InitRefs();
InitCursor();
}
private void InitCursor()
{
Cursor.visible = false;
SwitchCursor(defaultCursor);
}
private void OnEnable()
{
RegisterEvent();
}
private void OnDisable()
{
// 注销所有事件
foreach (var eventUnRegister in _unRegisters)
{
eventUnRegister.UnRegister();
}
_unRegisters.Clear();
}
private void InitRefs()
{
_cursor = transform.Find("Cursor").GetComponent<Image>();
_raycaster = UIManager.Instance.Canvas.GetComponent<GraphicRaycaster>();
}
private void RegisterEvent()
{
// 此处监听所有可能对鼠标状态产生影响的事件
RegisterGameEvent();
RegisterDialogEvent();
RegisterTriggerEvent();
}
private void RegisterGameEvent()
{
_unRegisters.Add(EnumEventSystem.Global.Register(GameLoopEnum.GameStart, OnGameStart));
_unRegisters.Add(EnumEventSystem.Global.Register(GameLoopEnum.GameQuit, OnGameQuit));
_unRegisters.Add(EnumEventSystem.Global.Register(GameLoopEnum.PauseGame, OnPauseGame));
_unRegisters.Add(EnumEventSystem.Global.Register(GameLoopEnum.UnPauseGame, OnUnPauseGame));
}
private void OnGameStart()
{
_isInGame = true;
}
private void OnUnPauseGame()
{
_curState = _stateCache;
_stateCache = CursorState.Default;
_isInGame = true;
}
private void OnPauseGame()
{
_stateCache = _curState;
_curState = CursorState.Default;
_isInGame = false;
}
private void OnGameQuit()
{
_curCursor = defaultCursor;
_curState = CursorState.Default;
_stateCache = CursorState.Default;
_isInGame = false;
}
private void RegisterDialogEvent()
{
// 对话相关
_unRegisters.Add(EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.LineStart,
OnLineStart));
_unRegisters.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineShown, () => SwitchCursor(nextTalk)));
_unRegisters.Add(EnumEventSystem.Global.Register(DialogEventEnum.LineEnd, OnLineEnd));
_unRegisters.Add(EnumEventSystem.Global.Register(DialogEventEnum.OptionShow, OnOptionShow));
_unRegisters.Add(
EnumEventSystem.Global.Register<DialogEventEnum, DialogText>(DialogEventEnum.OptionSelected,
OnOptionSelect));
}
private void RegisterTriggerEvent()
{
// 交互相关
_unRegisters.Add(EnumEventSystem.Global.Register(InteractionEventEnum.Start, OnInteractiveStart));
_unRegisters.Add(EnumEventSystem.Global.Register(InteractionEventEnum.End, OnInteractiveEnd));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerEnter, OnPointerEnter));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerExit, OnPointerExit));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerDown, OnPointerDown));
_unRegisters.Add(
EnumEventSystem.Global.Register<TriggerEnum, IInteraction>(TriggerEnum.PointerUp, OnPointerUp));
}
private void Update()
{
UpdateCursor();
HandleDialog();
}
private void UpdateCursor()
{
// 再判断是不是浮在可交互物上
_cursor.sprite = !_isInGame || IsOverSelected() ? defaultCursor : _curCursor;
}
private void HandleDialog()
{
if (!_isInGame) return;
if (!Input.GetMouseButtonDown(0) && !Input.GetKeyDown(KeyCode.Space)) return;
// 控制对话
if (_isTalking)
{
DialogController.Instance.JumpLine();
}
}
private bool IsOverSelected()
{
if (!EventSystem.current.IsPointerOverGameObject())
{
return false;
}
var eventData = new PointerEventData(EventSystem.current)
{
position = Input.mousePosition
};
var results = new List<RaycastResult>();
_raycaster.Raycast(eventData, results);
return results.Count > 0 && results.Any(result => result.gameObject.CompareTag("Selectable"));
}
private void LateUpdate()
{
// // 将虚拟指针与鼠标位置同步
// RectTransformUtility.ScreenPointToLocalPointInRectangle(_cursorCanvas, Input.mousePosition,
// CameraKit.Instance.uiCamera, out var worldPos);
_cursor.rectTransform.position = Input.mousePosition;
// 在输入状态,要单独处理
}
#region 对话相关
private void OnLineStart(DialogText line)
{
_isTalking = true;
SwitchCursor(talking);
}
private void OnLineEnd()
{
StopDelayCursor();
_switchCoroutine = StartCoroutine(DelayAction(() =>
{
_isTalking = false;
_curCursor = defaultCursor;
}));
}
private void OnOptionShow()
{
_isTalking = true;
SwitchCursor(option);
}
private void OnOptionSelect(DialogText line)
{
StopDelayCursor();
_switchCoroutine = StartCoroutine(DelayAction(() =>
{
_isTalking = false;
_curCursor = defaultCursor;
}));
}
#endregion
#region 交互相关
private void OnInteractiveStart()
{
_curState = CursorState.Interactive;
if (EventSystemEx.Instance.pointerOverObj)
{
SwitchCursor(interactive);
}
}
private void OnInteractiveEnd()
{
_curState = CursorState.Default;
// 退出时把Cursor置为Default
SwitchCursor(defaultCursor);
}
private void OnPointerEnter(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
// 如果不可交互,就显示不可交互的指针
SwitchCursor(target.IsActive ? interactive : disable);
}
}
private void OnPointerExit(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
SwitchCursor(defaultCursor);
}
}
private void OnPointerDown(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
SwitchCursor(target.IsActive ? holding : defaultCursor);
}
}
private void OnPointerUp(IInteraction target)
{
if (_curState == CursorState.Interactive && !_isTalking)
{
// 如果松手时还在物体之上
SwitchCursor(EventSystemEx.Instance.holdingObj == target.GetGameObject() ? interactive : defaultCursor);
}
}
#endregion
#region 指针切换
/// <summary>
/// 直接切指针
/// </summary>
/// <param name="sprite">指针图片</param>
private void SwitchCursor(Sprite sprite)
{
StopDelayCursor();
_curCursor = sprite;
}
private void StopDelayCursor()
{
if (_switchCoroutine != null)
{
StopCoroutine(_switchCoroutine);
_switchCoroutine = null;
}
}
#endregion
private static IEnumerator DelayAction(Action action)
{
yield return new WaitForSeconds(DelayTime);
action.Invoke();
}
}
public enum CursorState
{
Default,
Interactive
}
}