From 86375b8f67157dd5a2ac2612e047c9873aa111aa Mon Sep 17 00:00:00 2001 From: Steven Lai <2061455536@qq.com> Date: Tue, 25 Feb 2025 21:42:01 +0800 Subject: [PATCH] Fix Bug --- .../DragSubjectFocusTargetInteractCase.cs | 5 + EasyInteractive/EasyInteractive.cs | 99 ++++++++++++------- .../Example/Scripts/DragToScene.cs | 6 +- EasyInteractive/Example/Scripts/DragToUI.cs | 10 +- EasyInteractive/Example/Scripts/GhostIcon.cs | 3 + EasyInteractive/Example/Scripts/SceneItem.cs | 11 ++- EasyInteractive/Example/Scripts/Table.cs | 3 + EasyInteractive/Example/Scripts/UIItem.cs | 18 ++-- EasyInteractive/GameMonoUpdater.cs | 24 ++++- EasyInteractive/ICanUseGameMonoUpdater.cs | 21 +++- EasyInteractive/IInteractCase.cs | 10 ++ EasyInteractive/IInteractable.cs | 2 +- EasyInteractive/InteractCaseAttribute.cs | 17 +++- EasyInteractive/InteractableUIElement.cs | 59 +++++++---- 14 files changed, 201 insertions(+), 87 deletions(-) diff --git a/EasyInteractive/DragSubjectFocusTargetInteractCase.cs b/EasyInteractive/DragSubjectFocusTargetInteractCase.cs index ea7995b..0b768cc 100644 --- a/EasyInteractive/DragSubjectFocusTargetInteractCase.cs +++ b/EasyInteractive/DragSubjectFocusTargetInteractCase.cs @@ -19,6 +19,7 @@ namespace HalfDog.EasyInteractive public override bool Execute(IFocusable focusable, ISelectable selectable, IDragable dragable) { + //拖拽对象和聚焦对象都不能为空且对象类型匹配才能执行交互情景 if (focusable == null || dragable == null || focusable.interactTag != target || dragable.interactTag != subject) { @@ -55,6 +56,10 @@ namespace HalfDog.EasyInteractive protected virtual void OnExit() { } + + /// + /// 是否结束拖拽 + /// protected virtual bool EndDrag=>Input.GetMouseButtonUp(0); } } diff --git a/EasyInteractive/EasyInteractive.cs b/EasyInteractive/EasyInteractive.cs index 3a9ced5..d34bb10 100644 --- a/EasyInteractive/EasyInteractive.cs +++ b/EasyInteractive/EasyInteractive.cs @@ -35,8 +35,6 @@ namespace HalfDog.EasyInteractive private bool _isPointerOverUI; private Dictionary _allInteractCase = new Dictionary(); private List _executingInteractCases = new List(); - private List _activeInteractCase = new List(); - private Stack _currentUIItemFocusOnStack = new(); //当前激活的交互情景 private IInteractCase _currentActiveInteractCase; private bool _inUpdate; @@ -72,7 +70,7 @@ namespace HalfDog.EasyInteractive if (attribute != null) { IInteractCase ic = Activator.CreateInstance(types[i], attribute.interactSubject, attribute.interactTarget) as IInteractCase; _allInteractCase.Add(types[i],ic); - ic.enable = attribute.executeOnLoad; + ic.enable = attribute.enableExecuteOnLoad; _executingInteractCases.Add(ic); } } @@ -80,20 +78,20 @@ namespace HalfDog.EasyInteractive public void Update() { - //满足条件的InteractCase会被执行 - //这里的满足条件指的是 交互操作与交互对象类型都要一致 + //执行所有的交互情景 IInteractCase activeCase = null; for (int i = 0; i < _executingInteractCases.Count; i++) { + //Execute方法返回true则表示当前情景被激活 if (_executingInteractCases[i].enable && _executingInteractCases[i].Execute(currentFocused, currentSelected, currentDraged)) { activeCase = _executingInteractCases[i]; } } - //如果当前激活的情景更改了,则把激活的情景放到列表最前方第一个进行处理 - //这是为了在情景更改时首先执行激活情景的退出事件(如果有的话) + //为了在情景更改时首先执行激活情景的退出事件(如果有的话) if (activeCase != null && activeCase != _currentActiveInteractCase) { + //如果当前激活的情景更改了,则把激活的情景放到列表最前方第一个进行处理 _currentActiveInteractCase = activeCase; _executingInteractCases.Remove(_currentActiveInteractCase); _executingInteractCases.Insert(0, _currentActiveInteractCase); @@ -102,12 +100,12 @@ namespace HalfDog.EasyInteractive //当指针处于UI上时停止对场景中交互对象的操作 if (EventSystem.current?.IsPointerOverGameObject() ?? false) { + //如果当前指针不在UI上但是之前是在UI上则重置当前聚焦对象 if (!_isPointerOverUI) { - //ATTENTION 只有当前选择的和准备选择相同时才执行这个操作 否则会出现问题(PointerHandler先于OnFixedUpdate调用) if (currentSelected == _readySelect) _readySelect = null; - //ATTENTION 如果从场景转到UI上但是当前聚焦的对象没有RectTransform组件说明当前UI不是一个交互对象 + //如果从场景转到UI上但是当前聚焦的对象没有RectTransform组件说明当前UI不是一个交互对象 if (currentFocused != null && !(currentFocused as MonoBehaviour).TryGetComponent(out RectTransform component)) SetCurrentFocused(null); _isPointerOverUI = true; @@ -117,6 +115,7 @@ namespace HalfDog.EasyInteractive { if (Camera.main == null) return; _isPointerOverUI = false; + //射线检测 Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(mouseRay, out RaycastHit hitInfo, Mathf.Infinity)) { @@ -149,56 +148,71 @@ namespace HalfDog.EasyInteractive } } - - - //Debug.Log($"【Focus:{currentFocused?.interactTag.Name}】【Select:{currentSelected?.interactTag.Name}]】【Drag:{currentDraged?.interactTag.Name}】"); - - if (Input.GetMouseButtonDown(0) && currentFocused!=null) + bool mouseBtnPressed = Input.GetMouseButton(0); + if (currentFocused != null) { - _mouseDownPosition = Input.mousePosition; - _possibleDragTarget = (currentFocused as IDragable); + if (Input.GetMouseButtonDown(0)) + { + _mouseDownPosition = Input.mousePosition; + _possibleDragTarget = (currentFocused as IDragable); + } + //如果鼠标按下并且移动了一定距离则开始拖拽 + if (mouseBtnPressed) + { + if (currentDraged == null && Vector3.Distance(_mouseDownPosition, Input.mousePosition) > 16f) + { + if (_possibleDragTarget != null && _readyDrag == _possibleDragTarget) + { + //拖拽与被选中的不能是同一个 + if (_readyDrag == currentSelected) + { + //把选择的对象置空 + SetCurrentSelected(null); + } + //再设置拖拽 + SetCurrentDraged(_readyDrag); + } + } + } } + + //拖拽处理 + if (mouseBtnPressed && currentDraged != null) + currentDraged.ProcessDrag(); + if (Input.GetMouseButtonUp(0)) { if (currentDraged == null) { - if (_readySelect != null)//Vector3.Distance(_mouseDownPosition, Input.mousePosition) < 32f && + if (_readySelect != null) { if (currentSelected != _readySelect) SetCurrentSelected(_readySelect); //再次点击选择的对象则取消选中 else if (currentSelected == _readySelect) SetCurrentSelected(null); } } - else + else { SetCurrentDraged(null); } } - if (Input.GetMouseButton(0)) - { - if(currentDraged == null && Vector3.Distance(_mouseDownPosition, Input.mousePosition) > 16f) - { - if (_possibleDragTarget != null && _readyDrag == _possibleDragTarget) - { - //拖拽与被选中的不能是同一个 - if (_readyDrag == currentSelected) - { - SetCurrentSelected(null); - } - SetCurrentDraged(_readyDrag); - } - } - currentDraged?.ProcessDrag(); - } + //一些调试信息 //Debug.Log(currentFocused?.interactTag.Name ?? "Null"); - + //Debug.Log($"【Focus:{currentFocused?.interactTag.Name}】【Select:{currentSelected?.interactTag.Name}]】【Drag:{currentDraged?.interactTag.Name}】"); } + + /// + /// 重置所有的交互状态 + /// public void Reset() { SetCurrentDraged(null); SetCurrentSelected(null); SetCurrentFocused(null); } + /// + /// 设置当前聚焦对象 + /// public void SetCurrentFocused(IFocusable focusable,bool isUIItem = false) { _previousFocused = _currentFocused; @@ -206,19 +220,27 @@ namespace HalfDog.EasyInteractive _currentFocused = focusable; _currentFocused?.OnFocus(); } + /// + /// 设置当前选择对象 + /// public void SetCurrentSelected(ISelectable selectable) { _currentSelected?.EndSelect(); _currentSelected = selectable; _currentSelected?.OnSelect(); } + /// + /// 设置当前拖拽对象 + /// public void SetCurrentDraged(IDragable dragable) { - _currentDraged?.EndDrag(); + _currentDraged?.EndDrag(currentFocused); _currentDraged = dragable; _currentDraged?.OnDrag(); } - + /// + /// 启用指定的交互情景 + /// public void EnableInteractCase() where T : IInteractCase { Type type = typeof(T); @@ -227,6 +249,9 @@ namespace HalfDog.EasyInteractive _allInteractCase[type].enable = true; } } + /// + /// 禁用指定的交互情景 + /// public void DisableInteractCase() where T : IInteractCase { Type type = typeof(T); diff --git a/EasyInteractive/Example/Scripts/DragToScene.cs b/EasyInteractive/Example/Scripts/DragToScene.cs index 72a18aa..e3ff9d7 100644 --- a/EasyInteractive/Example/Scripts/DragToScene.cs +++ b/EasyInteractive/Example/Scripts/DragToScene.cs @@ -2,6 +2,9 @@ using HalfDog.EasyInteractive; using System; using UnityEngine; +/// +/// 从UIItem拖拽到场景中的交互情景 +/// [InteractCase(typeof(UIItem), typeof(Table))] public class DragToScene : DragSubjectFocusTargetInteractCase { @@ -13,7 +16,6 @@ public class DragToScene : DragSubjectFocusTargetInteractCase protected override void OnEnter(IDragable subject, IFocusable target) { - Debug.Log("Enter Case"); uiItem = (subject as UIItem); table = (target as Table); table.tempItem.gameObject.SetActive(true); @@ -24,13 +26,11 @@ public class DragToScene : DragSubjectFocusTargetInteractCase if (Input.GetMouseButtonUp(0)) { GameObject.FindObjectOfType(true).gameObject.SetActive(true); - Debug.Log("Execute"); } } protected override void OnExit() { - Debug.Log("Exit Case"); table.tempItem.gameObject.SetActive(false); uiItem.icon.gameObject.SetActive(false); } diff --git a/EasyInteractive/Example/Scripts/DragToUI.cs b/EasyInteractive/Example/Scripts/DragToUI.cs index fa098bb..5a91560 100644 --- a/EasyInteractive/Example/Scripts/DragToUI.cs +++ b/EasyInteractive/Example/Scripts/DragToUI.cs @@ -1,10 +1,9 @@ using HalfDog.EasyInteractive; using System; -using System.Collections; -using System.Collections.Generic; -using Unity.VisualScripting; -using UnityEngine; +/// +/// 拖拽物体到UI上的交互情景 +/// [InteractCase(typeof(SceneItem), typeof(UIItem))] public class DragToUI : DragSubjectFocusTargetInteractCase { @@ -15,7 +14,6 @@ public class DragToUI : DragSubjectFocusTargetInteractCase } protected override void OnEnter(IDragable subject, IFocusable target) { - Debug.Log("Enter Case"); sceneItem = (subject as SceneItem); uiItem = (target as UIItem); } @@ -23,14 +21,12 @@ public class DragToUI : DragSubjectFocusTargetInteractCase { if (EndDrag) { - Debug.Log("Execute"); uiItem.icon.gameObject.SetActive(true); uiItem.icon.sprite = sceneItem.iconSprite; } } protected override void OnExit() { - Debug.Log("Exit Case"); sceneItem.gameObject.SetActive(false); } } diff --git a/EasyInteractive/Example/Scripts/GhostIcon.cs b/EasyInteractive/Example/Scripts/GhostIcon.cs index 8c451fc..51e39f2 100644 --- a/EasyInteractive/Example/Scripts/GhostIcon.cs +++ b/EasyInteractive/Example/Scripts/GhostIcon.cs @@ -1,6 +1,9 @@ using UnityEngine; using UnityEngine.UI; +/// +/// 鼠标拖拽图标 +/// public class GhostIcon : MonoBehaviour { public static GhostIcon Instance; diff --git a/EasyInteractive/Example/Scripts/SceneItem.cs b/EasyInteractive/Example/Scripts/SceneItem.cs index af58a16..ce53d95 100644 --- a/EasyInteractive/Example/Scripts/SceneItem.cs +++ b/EasyInteractive/Example/Scripts/SceneItem.cs @@ -2,6 +2,9 @@ using HalfDog.EasyInteractive; using System; using UnityEngine; +/// +/// 场景对象 +/// public class SceneItem : MonoBehaviour, IDragable { public Sprite iconSprite; @@ -25,17 +28,17 @@ public class SceneItem : MonoBehaviour, IDragable } public void OnDrag() { - Debug.Log("Begin Drag"); GhostIcon.Instance.ShowGhostIcon(iconSprite); gameObject.SetActive(false); } public void ProcessDrag() { } - public void EndDrag() + public void EndDrag(IFocusable target) { - Debug.Log("End Drag"); GhostIcon.Instance.HideGhostIcon(); - gameObject.SetActive(true); + //判断是否拖拽到了目标 + if (target == null) + gameObject.SetActive(true); } } diff --git a/EasyInteractive/Example/Scripts/Table.cs b/EasyInteractive/Example/Scripts/Table.cs index fadb8db..925301c 100644 --- a/EasyInteractive/Example/Scripts/Table.cs +++ b/EasyInteractive/Example/Scripts/Table.cs @@ -2,6 +2,9 @@ using System; using UnityEngine; using HalfDog.EasyInteractive; +/// +/// 放置物体的台面 +/// public class Table : MonoBehaviour, IFocusable { public GameObject tempItem; diff --git a/EasyInteractive/Example/Scripts/UIItem.cs b/EasyInteractive/Example/Scripts/UIItem.cs index fc31973..a67d580 100644 --- a/EasyInteractive/Example/Scripts/UIItem.cs +++ b/EasyInteractive/Example/Scripts/UIItem.cs @@ -3,6 +3,9 @@ using System; using UnityEngine; using UnityEngine.UI; +/// +/// UI对象 +/// public class UIItem : InteractableUIElement,IDragable { public Image icon; @@ -11,18 +14,18 @@ public class UIItem : InteractableUIElement,IDragable public Type interactTag => typeof(UIItem); public bool enableFocus => true; public bool enableDrag => _enableDrag; - - public void OnFocus() + private void Update() { _enableDrag = icon.gameObject.activeSelf; } + public void OnFocus() + { + } public void EndFocus() { - _enableDrag = true; } public void OnDrag() { - Debug.Log("Begin Drag"); if (!icon.gameObject.activeSelf) return; GhostIcon.Instance.ShowGhostIcon(icon.sprite); icon.gameObject.SetActive(false); @@ -30,10 +33,11 @@ public class UIItem : InteractableUIElement,IDragable public void ProcessDrag() { } - public void EndDrag() + public void EndDrag(IFocusable target) { - Debug.Log("End Drag"); GhostIcon.Instance.HideGhostIcon(); - icon.gameObject.SetActive(true); + //判断是否拖拽到了目标 + if (target == null) + icon.gameObject.SetActive(true); } } diff --git a/EasyInteractive/GameMonoUpdater.cs b/EasyInteractive/GameMonoUpdater.cs index e73b886..d8bfa49 100644 --- a/EasyInteractive/GameMonoUpdater.cs +++ b/EasyInteractive/GameMonoUpdater.cs @@ -3,6 +3,9 @@ using UnityEngine; namespace HalfDog.GameMonoUpdater { + /// + /// 游戏MonoUpdater + /// public class GameMonoUpdater : MonoBehaviour { private static GameMonoUpdater _instance = null; @@ -22,12 +25,29 @@ namespace HalfDog.GameMonoUpdater return _instance; } } - + /// + /// 添加Update事件 + /// public static void AddUpdateAction(Action action) => instance.updateAction += action; - public static void RemoveUpdateAction(Action action) => instance.updateAction -= action; + /// + /// 移除Update事件 + /// + public static void RemoveUpdateAction(Action action) => instance.updateAction -= action; + /// + /// 添加FixedUpdate事件 + /// public static void AddFixedUpdateAction(Action action) => instance.fixedUpdateAction += action; + /// + /// 移除FixedUpdate事件 + /// public static void RemoveFixedUpdateAction(Action action) => instance.fixedUpdateAction -= action; + /// + /// 添加LateUpdate事件 + /// public static void AddLateUpdateAction(Action action) => instance.lateUpdateAction += action; + /// + /// 移除LateUpdate事件 + /// public static void RemoveLateUpdateAction(Action action) => instance.lateUpdateAction -= action; void Awake() diff --git a/EasyInteractive/ICanUseGameMonoUpdater.cs b/EasyInteractive/ICanUseGameMonoUpdater.cs index df1ec6e..2456a48 100644 --- a/EasyInteractive/ICanUseGameMonoUpdater.cs +++ b/EasyInteractive/ICanUseGameMonoUpdater.cs @@ -1,6 +1,9 @@ namespace HalfDog.GameMonoUpdater { - public interface ICanUseGameMonoUpdater + /// + /// 可使用游戏MonoUpdater + /// + public interface ICanUseGameMonoUpdater { public void FixedUpdate(); public void Update(); @@ -8,9 +11,15 @@ namespace HalfDog.GameMonoUpdater public bool InUpdate { get; set; } } - public static class CanUseMonoUpdaterExtension + /// + /// 游戏MonoUpdater扩展 + /// + public static class CanUseMonoUpdaterExtension { - public static void EnableMonoUpdater(this ICanUseGameMonoUpdater self) + /// + /// 启用MonoUpdater + /// + public static void EnableMonoUpdater(this ICanUseGameMonoUpdater self) { if (self.InUpdate) return; GameMonoUpdater.AddUpdateAction(self.Update); @@ -18,8 +27,10 @@ namespace HalfDog.GameMonoUpdater GameMonoUpdater.AddLateUpdateAction(self.LateUpdate); self.InUpdate = true; } - - public static void DisableMonoUpdater(this ICanUseGameMonoUpdater self) + /// + /// 禁用MonoUpdater + /// + public static void DisableMonoUpdater(this ICanUseGameMonoUpdater self) { GameMonoUpdater.RemoveUpdateAction(self.Update); GameMonoUpdater.RemoveFixedUpdateAction(self.FixedUpdate); diff --git a/EasyInteractive/IInteractCase.cs b/EasyInteractive/IInteractCase.cs index f9e6683..325fb96 100644 --- a/EasyInteractive/IInteractCase.cs +++ b/EasyInteractive/IInteractCase.cs @@ -10,6 +10,13 @@ namespace HalfDog.EasyInteractive public Type subject { get; } public Type target { get; } public bool enable { get; set; } + /// + /// 执行交互情景 + /// + /// 当前聚焦对象 + /// 当前选择对象 + /// 当前拖拽对象 + /// public bool Execute(IFocusable focusable, ISelectable selectable, IDragable dragable); } @@ -24,6 +31,9 @@ namespace HalfDog.EasyInteractive public Type subject => _subject; public Type target => _target; + /// + /// 是否开启 + /// public bool enable { get => _enable; diff --git a/EasyInteractive/IInteractable.cs b/EasyInteractive/IInteractable.cs index d8635fd..4d4f49b 100644 --- a/EasyInteractive/IInteractable.cs +++ b/EasyInteractive/IInteractable.cs @@ -60,6 +60,6 @@ namespace HalfDog.EasyInteractive /// /// 结束拖拽 /// - public void EndDrag(); + public void EndDrag(IFocusable target); } } \ No newline at end of file diff --git a/EasyInteractive/InteractCaseAttribute.cs b/EasyInteractive/InteractCaseAttribute.cs index 18ff5e4..ba95ab2 100644 --- a/EasyInteractive/InteractCaseAttribute.cs +++ b/EasyInteractive/InteractCaseAttribute.cs @@ -2,18 +2,27 @@ using System; namespace HalfDog.EasyInteractive { - [AttributeUsage(AttributeTargets.Class)] + /// + /// 交互情景标识 + /// + [AttributeUsage(AttributeTargets.Class)] public class InteractCaseAttribute : Attribute { public Type interactSubject; public Type interactTarget; - public bool executeOnLoad; + public bool enableExecuteOnLoad; - public InteractCaseAttribute(Type subject, Type target, bool executeOnLoad = true) + /// + /// 交互情景标识 + /// + /// 交互主体类型 + /// 交互目标类型 + /// 默认开启执行 + public InteractCaseAttribute(Type subject, Type target, bool enableExecuteOnLoad = true) { interactSubject = subject; interactTarget = target; - this.executeOnLoad = executeOnLoad; + this.enableExecuteOnLoad = enableExecuteOnLoad; } } } diff --git a/EasyInteractive/InteractableUIElement.cs b/EasyInteractive/InteractableUIElement.cs index 72cd83f..1ffb472 100644 --- a/EasyInteractive/InteractableUIElement.cs +++ b/EasyInteractive/InteractableUIElement.cs @@ -3,48 +3,73 @@ using UnityEngine.EventSystems; namespace HalfDog.EasyInteractive { - public abstract class InteractableUIElement : MonoBehaviour, + /// + /// 可交互UI元素 + /// + public abstract class InteractableUIElement : MonoBehaviour, IPointerEnterHandler, - IPointerExitHandler + IPointerExitHandler, + IPointerMoveHandler { public bool enableInteract = true; - public void OnPointerEnter(PointerEventData eventData) + + private IFocusable _focusable = null; + private ISelectable _selectable = null; + private IDragable _dragable = null; + + public void OnPointerEnter(PointerEventData eventData) { if (!enableInteract) return; - if ((this as IFocusable) != null && (this as IFocusable).enableFocus) + _focusable = (this as IFocusable); + _selectable = (this as ISelectable); + _dragable = (this as IDragable); + if (_focusable != null && _focusable.enableFocus) { - EasyInteractive.Instance.SetCurrentFocused(this as IFocusable,true); + EasyInteractive.Instance.SetCurrentFocused(_focusable, true); } - if ((this as ISelectable) != null && (this as ISelectable).enableSelect) + if (_selectable != null && _selectable.enableSelect) { - EasyInteractive.Instance.readySelect = (this as ISelectable); + EasyInteractive.Instance.readySelect = _selectable; } - if ((this as IDragable) != null && (this as IDragable).enableDrag) + if (_dragable != null && _dragable.enableDrag) { - EasyInteractive.Instance.readyDrag = (this as IDragable); + EasyInteractive.Instance.readyDrag = _dragable; } } - public void OnPointerExit(PointerEventData eventData) + public void OnPointerMove(PointerEventData eventData) + { + if (!enableInteract) return; + if (_selectable != null && _selectable.enableSelect) + { + EasyInteractive.Instance.readySelect = _selectable; + } + if (_dragable != null && _dragable.enableDrag) + { + EasyInteractive.Instance.readyDrag = _dragable; + } + } + + public void OnPointerExit(PointerEventData eventData) { if (!enableInteract) return; - if ((this as IFocusable) != null && (this as IFocusable).enableFocus) + if (_focusable != null && _focusable.enableFocus) { - if (EasyInteractive.Instance.currentFocused == (this as IFocusable)) + if (EasyInteractive.Instance.currentFocused == _focusable) { EasyInteractive.Instance.SetCurrentFocused(null,true); } } - if ((this as ISelectable) != null && (this as ISelectable).enableSelect) + if (_selectable != null && _selectable.enableSelect) { - if((this as ISelectable) == EasyInteractive.Instance.readySelect) + if(EasyInteractive.Instance.readySelect == _selectable) EasyInteractive.Instance.readySelect = null; } - if ((this as IDragable) != null && (this as IDragable).enableDrag) + if (_dragable != null && _dragable.enableDrag) { - if ((this as IDragable) == EasyInteractive.Instance.readyDrag) + if (EasyInteractive.Instance.readyDrag == _dragable) EasyInteractive.Instance.readyDrag = null; } } - } + } }