330 lines
9.5 KiB
C#
330 lines
9.5 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;
|
|
|
|
[SerializeField]
|
|
private GameObject border;
|
|
|
|
[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()
|
|
{
|
|
border.SetActive(true);
|
|
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
|
|
}
|
|
} |