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 { 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 _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(); _raycaster = UIManager.Instance.Canvas.GetComponent(); } 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.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.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.PointerEnter, OnPointerEnter)); _unRegisters.Add( EnumEventSystem.Global.Register(TriggerEnum.PointerExit, OnPointerExit)); _unRegisters.Add( EnumEventSystem.Global.Register(TriggerEnum.PointerDown, OnPointerDown)); _unRegisters.Add( EnumEventSystem.Global.Register(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(); _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 指针切换 /// /// 直接切指针 /// /// 指针图片 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 } }