This commit is contained in:
Steven Lai
2025-02-25 21:42:01 +08:00
parent 389b45cf99
commit 86375b8f67
14 changed files with 201 additions and 87 deletions
@@ -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()
{
}
/// <summary>
/// 是否结束拖拽
/// </summary>
protected virtual bool EndDrag=>Input.GetMouseButtonUp(0);
}
}
+62 -37
View File
@@ -35,8 +35,6 @@ namespace HalfDog.EasyInteractive
private bool _isPointerOverUI;
private Dictionary<Type,IInteractCase> _allInteractCase = new Dictionary<Type,IInteractCase>();
private List<IInteractCase> _executingInteractCases = new List<IInteractCase>();
private List<IInteractCase> _activeInteractCase = new List<IInteractCase>();
private Stack<IFocusable> _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}】");
}
/// <summary>
/// 重置所有的交互状态
/// </summary>
public void Reset()
{
SetCurrentDraged(null);
SetCurrentSelected(null);
SetCurrentFocused(null);
}
/// <summary>
/// 设置当前聚焦对象
/// </summary>
public void SetCurrentFocused(IFocusable focusable,bool isUIItem = false)
{
_previousFocused = _currentFocused;
@@ -206,19 +220,27 @@ namespace HalfDog.EasyInteractive
_currentFocused = focusable;
_currentFocused?.OnFocus();
}
/// <summary>
/// 设置当前选择对象
/// </summary>
public void SetCurrentSelected(ISelectable selectable)
{
_currentSelected?.EndSelect();
_currentSelected = selectable;
_currentSelected?.OnSelect();
}
/// <summary>
/// 设置当前拖拽对象
/// </summary>
public void SetCurrentDraged(IDragable dragable)
{
_currentDraged?.EndDrag();
_currentDraged?.EndDrag(currentFocused);
_currentDraged = dragable;
_currentDraged?.OnDrag();
}
/// <summary>
/// 启用指定的交互情景
/// </summary>
public void EnableInteractCase<T>() where T : IInteractCase
{
Type type = typeof(T);
@@ -227,6 +249,9 @@ namespace HalfDog.EasyInteractive
_allInteractCase[type].enable = true;
}
}
/// <summary>
/// 禁用指定的交互情景
/// </summary>
public void DisableInteractCase<T>() where T : IInteractCase
{
Type type = typeof(T);
@@ -2,6 +2,9 @@ using HalfDog.EasyInteractive;
using System;
using UnityEngine;
/// <summary>
/// 从UIItem拖拽到场景中的交互情景
/// </summary>
[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<SceneItem>(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);
}
+3 -7
View File
@@ -1,10 +1,9 @@
using HalfDog.EasyInteractive;
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
/// <summary>
/// 拖拽物体到UI上的交互情景
/// </summary>
[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);
}
}
@@ -1,6 +1,9 @@
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 鼠标拖拽图标
/// </summary>
public class GhostIcon : MonoBehaviour
{
public static GhostIcon Instance;
+7 -4
View File
@@ -2,6 +2,9 @@ using HalfDog.EasyInteractive;
using System;
using UnityEngine;
/// <summary>
/// 场景对象
/// </summary>
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);
}
}
+3
View File
@@ -2,6 +2,9 @@ using System;
using UnityEngine;
using HalfDog.EasyInteractive;
/// <summary>
/// 放置物体的台面
/// </summary>
public class Table : MonoBehaviour, IFocusable
{
public GameObject tempItem;
+11 -7
View File
@@ -3,6 +3,9 @@ using System;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// UI对象
/// </summary>
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);
}
}
+22 -2
View File
@@ -3,6 +3,9 @@ using UnityEngine;
namespace HalfDog.GameMonoUpdater
{
/// <summary>
/// 游戏MonoUpdater
/// </summary>
public class GameMonoUpdater : MonoBehaviour
{
private static GameMonoUpdater _instance = null;
@@ -22,12 +25,29 @@ namespace HalfDog.GameMonoUpdater
return _instance;
}
}
/// <summary>
/// 添加Update事件
/// </summary>
public static void AddUpdateAction(Action action) => instance.updateAction += action;
public static void RemoveUpdateAction(Action action) => instance.updateAction -= action;
/// <summary>
/// 移除Update事件
/// </summary>
public static void RemoveUpdateAction(Action action) => instance.updateAction -= action;
/// <summary>
/// 添加FixedUpdate事件
/// </summary>
public static void AddFixedUpdateAction(Action action) => instance.fixedUpdateAction += action;
/// <summary>
/// 移除FixedUpdate事件
/// </summary>
public static void RemoveFixedUpdateAction(Action action) => instance.fixedUpdateAction -= action;
/// <summary>
/// 添加LateUpdate事件
/// </summary>
public static void AddLateUpdateAction(Action action) => instance.lateUpdateAction += action;
/// <summary>
/// 移除LateUpdate事件
/// </summary>
public static void RemoveLateUpdateAction(Action action) => instance.lateUpdateAction -= action;
void Awake()
+16 -5
View File
@@ -1,6 +1,9 @@
namespace HalfDog.GameMonoUpdater
{
public interface ICanUseGameMonoUpdater
/// <summary>
/// 可使用游戏MonoUpdater
/// </summary>
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
/// <summary>
/// 游戏MonoUpdater扩展
/// </summary>
public static class CanUseMonoUpdaterExtension
{
public static void EnableMonoUpdater(this ICanUseGameMonoUpdater self)
/// <summary>
/// 启用MonoUpdater
/// </summary>
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)
/// <summary>
/// 禁用MonoUpdater
/// </summary>
public static void DisableMonoUpdater(this ICanUseGameMonoUpdater self)
{
GameMonoUpdater.RemoveUpdateAction(self.Update);
GameMonoUpdater.RemoveFixedUpdateAction(self.FixedUpdate);
+10
View File
@@ -10,6 +10,13 @@ namespace HalfDog.EasyInteractive
public Type subject { get; }
public Type target { get; }
public bool enable { get; set; }
/// <summary>
/// 执行交互情景
/// </summary>
/// <param name="focusable">当前聚焦对象</param>
/// <param name="selectable">当前选择对象</param>
/// <param name="dragable">当前拖拽对象</param>
/// <returns></returns>
public bool Execute(IFocusable focusable, ISelectable selectable, IDragable dragable);
}
@@ -24,6 +31,9 @@ namespace HalfDog.EasyInteractive
public Type subject => _subject;
public Type target => _target;
/// <summary>
/// 是否开启
/// </summary>
public bool enable
{
get => _enable;
+1 -1
View File
@@ -60,6 +60,6 @@ namespace HalfDog.EasyInteractive
/// <summary>
/// 结束拖拽
/// </summary>
public void EndDrag();
public void EndDrag(IFocusable target);
}
}
+13 -4
View File
@@ -2,18 +2,27 @@ using System;
namespace HalfDog.EasyInteractive
{
[AttributeUsage(AttributeTargets.Class)]
/// <summary>
/// 交互情景标识
/// </summary>
[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)
/// <summary>
/// 交互情景标识
/// </summary>
/// <param name="subject">交互主体类型</param>
/// <param name="target">交互目标类型</param>
/// <param name="enableExecuteOnLoad">默认开启执行</param>
public InteractCaseAttribute(Type subject, Type target, bool enableExecuteOnLoad = true)
{
interactSubject = subject;
interactTarget = target;
this.executeOnLoad = executeOnLoad;
this.enableExecuteOnLoad = enableExecuteOnLoad;
}
}
}
+42 -17
View File
@@ -3,48 +3,73 @@ using UnityEngine.EventSystems;
namespace HalfDog.EasyInteractive
{
public abstract class InteractableUIElement : MonoBehaviour,
/// <summary>
/// 可交互UI元素
/// </summary>
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;
}
}
}
}
}