Init
This commit is contained in:
59
EasyInteractive/DragSubjectFocusTargetInteractCase.cs
Normal file
59
EasyInteractive/DragSubjectFocusTargetInteractCase.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
namespace HalfDog.EasyInteractive
|
||||
{
|
||||
/// <summary>
|
||||
/// 拖拽主体并聚焦目标的交互情景
|
||||
/// </summary>
|
||||
public abstract class DragSubjectFocusTargetInteractCase : AbstractInteractCase
|
||||
{
|
||||
private bool _isEnter = false;
|
||||
private bool _isExit = true;
|
||||
|
||||
public DragSubjectFocusTargetInteractCase(Type subject, Type target) : base(subject, target)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract void OnExecute(IDragable subject, IFocusable target);
|
||||
|
||||
public override bool Execute(IFocusable focusable, ISelectable selectable, IDragable dragable)
|
||||
{
|
||||
if (focusable == null || dragable == null || focusable.interactTag != target ||
|
||||
dragable.interactTag != subject)
|
||||
{
|
||||
if (_isEnter)
|
||||
{
|
||||
_isEnter = false;
|
||||
OnExit(dragable, focusable);
|
||||
_isExit = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_isExit)
|
||||
{
|
||||
_isExit = false;
|
||||
OnEnter(dragable, focusable);
|
||||
_isEnter = true;
|
||||
}
|
||||
|
||||
OnExecute(dragable, focusable);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进入交互情景
|
||||
/// </summary>
|
||||
protected virtual void OnEnter(IDragable subject, IFocusable target)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 退出交互情景
|
||||
/// </summary>
|
||||
protected virtual void OnExit(IDragable subject, IFocusable target)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
239
EasyInteractive/EasyInteractive.cs
Normal file
239
EasyInteractive/EasyInteractive.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using HalfDog.GameMonoUpdater;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace HalfDog.EasyInteractive
|
||||
{
|
||||
public class EasyInteractive : ICanUseGameMonoUpdater
|
||||
{
|
||||
private static EasyInteractive _instance;
|
||||
|
||||
public static EasyInteractive Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new EasyInteractive();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private IFocusable _currentFocused;
|
||||
private IFocusable _previousFocused;
|
||||
private ISelectable _currentSelected;
|
||||
private IDragable _currentDraged;
|
||||
private ISelectable _readySelect;
|
||||
private IDragable _readyDrag;
|
||||
private IDragable _possibleDragTarget;
|
||||
private Vector3 _mouseDownPosition;
|
||||
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;
|
||||
|
||||
public bool InUpdate
|
||||
{
|
||||
get => _inUpdate;
|
||||
set => _inUpdate = value;
|
||||
}
|
||||
|
||||
public bool isPointerOverUI => _isPointerOverUI;
|
||||
public IFocusable currentFocused => _currentFocused;
|
||||
public ISelectable currentSelected => _currentSelected;
|
||||
public ISelectable readySelect { get => _readySelect; set => _readySelect = value; }
|
||||
public IDragable currentDraged => _currentDraged;
|
||||
public IDragable readyDrag { get => _readyDrag; set => _readyDrag = value; }
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void MakesureInstanceExist()
|
||||
{
|
||||
_instance = new EasyInteractive();
|
||||
}
|
||||
|
||||
public EasyInteractive()
|
||||
{
|
||||
this.EnableMonoUpdater();
|
||||
//加载所有的交互情景
|
||||
Assembly assembly = typeof(EasyInteractive).Assembly;
|
||||
List<Type> types = assembly.GetTypes().Where(type => typeof(IInteractCase).IsAssignableFrom(type) && !type.IsAbstract).ToList();
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
{
|
||||
InteractCaseAttribute attribute = types[i].GetCustomAttribute<InteractCaseAttribute>();
|
||||
if (attribute != null) {
|
||||
IInteractCase ic = Activator.CreateInstance(types[i], attribute.interactSubject, attribute.interactTarget) as IInteractCase;
|
||||
_allInteractCase.Add(types[i],ic);
|
||||
ic.enable = attribute.executeOnLoad;
|
||||
_executingInteractCases.Add(ic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
//当指针处于UI上时停止对场景中交互对象的操作
|
||||
if (EventSystem.current?.IsPointerOverGameObject() ?? false)
|
||||
{
|
||||
if (!_isPointerOverUI)
|
||||
{
|
||||
//ATTENTION 只有当前选择的和准备选择相同时才执行这个操作 否则会出现问题(PointerHandler先于OnFixedUpdate调用)
|
||||
if (currentSelected == _readySelect)
|
||||
_readySelect = null;
|
||||
//ATTENTION 如果从场景转到UI上但是当前聚焦的对象没有RectTransform组件说明当前UI不是一个交互对象
|
||||
if (currentFocused != null && !(currentFocused as MonoBehaviour).TryGetComponent(out RectTransform component))
|
||||
SetCurrentFocused(null);
|
||||
_isPointerOverUI = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Camera.main == null) return;
|
||||
_isPointerOverUI = false;
|
||||
Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
if (Physics.Raycast(mouseRay, out RaycastHit hitInfo, Mathf.Infinity))
|
||||
{
|
||||
if (hitInfo.transform.TryGetComponent(out IFocusable focus))
|
||||
{
|
||||
if (focus != _currentFocused && focus.enableFocus)
|
||||
SetCurrentFocused(focus);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCurrentFocused(null);
|
||||
}
|
||||
|
||||
if (hitInfo.transform.TryGetComponent(out ISelectable selectable) && selectable.enableSelect)
|
||||
_readySelect = selectable;
|
||||
else
|
||||
_readySelect = null;
|
||||
|
||||
if (hitInfo.transform.TryGetComponent(out IDragable dragable) && dragable.enableDrag)
|
||||
_readyDrag = dragable;
|
||||
else
|
||||
_readyDrag = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_readyDrag = null;
|
||||
_readySelect = null;
|
||||
//射线没有任何碰撞应该把当前聚焦的对象置空
|
||||
SetCurrentFocused(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//满足条件的InteractCase会被执行
|
||||
//这里的满足条件指的是 交互操作与交互对象类型都要一致
|
||||
IInteractCase activeCase = null;
|
||||
for (int i = 0; i < _executingInteractCases.Count; i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
//Debug.Log($"【Focus:{currentFocused?.interactTag.Name}】【Select:{currentSelected?.interactTag.Name}]】【Drag:{currentDraged?.interactTag.Name}】");
|
||||
|
||||
if (Input.GetMouseButtonDown(0) && currentFocused!=null)
|
||||
{
|
||||
_mouseDownPosition = Input.mousePosition;
|
||||
_possibleDragTarget = (currentFocused as IDragable);
|
||||
}
|
||||
if (Input.GetMouseButtonUp(0))
|
||||
{
|
||||
if (currentDraged == null)
|
||||
{
|
||||
if (_readySelect != null)//Vector3.Distance(_mouseDownPosition, Input.mousePosition) < 32f &&
|
||||
{
|
||||
if (currentSelected != _readySelect) SetCurrentSelected(_readySelect);
|
||||
//再次点击选择的对象则取消选中
|
||||
else if (currentSelected == _readySelect) SetCurrentSelected(null);
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
public void Reset()
|
||||
{
|
||||
SetCurrentDraged(null);
|
||||
SetCurrentSelected(null);
|
||||
SetCurrentFocused(null);
|
||||
}
|
||||
public void SetCurrentFocused(IFocusable focusable,bool isUIItem = false)
|
||||
{
|
||||
_previousFocused = _currentFocused;
|
||||
_currentFocused?.EndFocus();
|
||||
_currentFocused = focusable;
|
||||
_currentFocused?.OnFocus();
|
||||
}
|
||||
public void SetCurrentSelected(ISelectable selectable)
|
||||
{
|
||||
_currentSelected?.EndSelect();
|
||||
_currentSelected = selectable;
|
||||
_currentSelected?.OnSelect();
|
||||
}
|
||||
public void SetCurrentDraged(IDragable dragable)
|
||||
{
|
||||
_currentDraged?.EndDrag();
|
||||
_currentDraged = dragable;
|
||||
_currentDraged?.OnDrag();
|
||||
}
|
||||
|
||||
public void EnableInteractCase<T>() where T : IInteractCase
|
||||
{
|
||||
Type type = typeof(T);
|
||||
if (_allInteractCase.ContainsKey(type))
|
||||
{
|
||||
_allInteractCase[type].enable = true;
|
||||
}
|
||||
}
|
||||
public void DisableInteractCase<T>() where T : IInteractCase
|
||||
{
|
||||
Type type = typeof(T);
|
||||
if (_allInteractCase.ContainsKey(type))
|
||||
{
|
||||
_allInteractCase[type].enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void FixedUpdate() { }
|
||||
public void LateUpdate() { }
|
||||
}
|
||||
}
|
||||
57
EasyInteractive/GameMonoUpdater.cs
Normal file
57
EasyInteractive/GameMonoUpdater.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HalfDog.GameMonoUpdater
|
||||
{
|
||||
public class GameMonoUpdater : MonoBehaviour
|
||||
{
|
||||
private static GameMonoUpdater _instance = null;
|
||||
public event Action updateAction;
|
||||
public event Action fixedUpdateAction;
|
||||
public event Action lateUpdateAction;
|
||||
private static GameMonoUpdater instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
GameObject obj = new GameObject("[GameMonoUpdater]");
|
||||
_instance = obj.AddComponent<GameMonoUpdater>();
|
||||
DontDestroyOnLoad(obj);
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddUpdateAction(Action action) => instance.updateAction += action;
|
||||
public static void RemoveUpdateAction(Action action) => instance.updateAction -= action;
|
||||
public static void AddFixedUpdateAction(Action action) => instance.fixedUpdateAction += action;
|
||||
public static void RemoveFixedUpdateAction(Action action) => instance.fixedUpdateAction -= action;
|
||||
public static void AddLateUpdateAction(Action action) => instance.lateUpdateAction += action;
|
||||
public static void RemoveLateUpdateAction(Action action) => instance.lateUpdateAction -= action;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (_instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
updateAction?.Invoke();
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
fixedUpdateAction?.Invoke();
|
||||
}
|
||||
private void LateUpdate()
|
||||
{
|
||||
lateUpdateAction?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
30
EasyInteractive/ICanUseGameMonoUpdater.cs
Normal file
30
EasyInteractive/ICanUseGameMonoUpdater.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace HalfDog.GameMonoUpdater
|
||||
{
|
||||
public interface ICanUseGameMonoUpdater
|
||||
{
|
||||
public void FixedUpdate();
|
||||
public void Update();
|
||||
public void LateUpdate();
|
||||
public bool InUpdate { get; set; }
|
||||
}
|
||||
|
||||
public static class CanUseMonoUpdaterExtension
|
||||
{
|
||||
public static void EnableMonoUpdater(this ICanUseGameMonoUpdater self)
|
||||
{
|
||||
if (self.InUpdate) return;
|
||||
GameMonoUpdater.AddUpdateAction(self.Update);
|
||||
GameMonoUpdater.AddFixedUpdateAction(self.FixedUpdate);
|
||||
GameMonoUpdater.AddLateUpdateAction(self.LateUpdate);
|
||||
self.InUpdate = true;
|
||||
}
|
||||
|
||||
public static void DisableMonoUpdater(this ICanUseGameMonoUpdater self)
|
||||
{
|
||||
GameMonoUpdater.RemoveUpdateAction(self.Update);
|
||||
GameMonoUpdater.RemoveFixedUpdateAction(self.FixedUpdate);
|
||||
GameMonoUpdater.RemoveLateUpdateAction(self.LateUpdate);
|
||||
self.InUpdate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
EasyInteractive/IInteractCase.cs
Normal file
41
EasyInteractive/IInteractCase.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
namespace HalfDog.EasyInteractive
|
||||
{
|
||||
/// <summary>
|
||||
/// 交互情景接口
|
||||
/// </summary>
|
||||
public interface IInteractCase
|
||||
{
|
||||
public Type subject { get; }
|
||||
public Type target { get; }
|
||||
public bool enable { get; set; }
|
||||
public bool Execute(IFocusable focusable, ISelectable selectable, IDragable dragable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交互情景抽象类
|
||||
/// </summary>
|
||||
public abstract class AbstractInteractCase : IInteractCase
|
||||
{
|
||||
private Type _subject;
|
||||
private Type _target;
|
||||
private bool _enable;
|
||||
public Type subject => _subject;
|
||||
public Type target => _target;
|
||||
|
||||
public bool enable
|
||||
{
|
||||
get => _enable;
|
||||
set => _enable = value;
|
||||
}
|
||||
|
||||
public AbstractInteractCase(Type subject, Type target)
|
||||
{
|
||||
_subject = subject;
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public abstract bool Execute(IFocusable focusable, ISelectable selectable, IDragable dragable);
|
||||
}
|
||||
}
|
||||
65
EasyInteractive/IInteractable.cs
Normal file
65
EasyInteractive/IInteractable.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
|
||||
namespace HalfDog.EasyInteractive
|
||||
{
|
||||
public interface IInteractable
|
||||
{
|
||||
/// <summary>
|
||||
/// 交互对象标识
|
||||
/// </summary>
|
||||
public Type interactTag { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交互:可聚焦接口
|
||||
/// </summary>
|
||||
public interface IFocusable : IInteractable
|
||||
{
|
||||
public bool enableFocus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 焦点进入
|
||||
/// </summary>
|
||||
public void OnFocus();
|
||||
|
||||
/// <summary>
|
||||
/// 焦点离开
|
||||
/// </summary>
|
||||
public void EndFocus();
|
||||
}
|
||||
|
||||
public interface ISelectable : IFocusable, IInteractable
|
||||
{
|
||||
public bool enableSelect { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 选择
|
||||
/// </summary>
|
||||
public void OnSelect();
|
||||
|
||||
/// <summary>
|
||||
/// 取消选择
|
||||
/// </summary>
|
||||
public void EndSelect();
|
||||
}
|
||||
|
||||
public interface IDragable : IFocusable, IInteractable
|
||||
{
|
||||
public bool enableDrag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始拖拽
|
||||
/// </summary>
|
||||
public void OnDrag();
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽中
|
||||
/// </summary>
|
||||
public void ProcessDrag();
|
||||
|
||||
/// <summary>
|
||||
/// 结束拖拽
|
||||
/// </summary>
|
||||
public void EndDrag();
|
||||
}
|
||||
}
|
||||
19
EasyInteractive/InteractCaseAttribute.cs
Normal file
19
EasyInteractive/InteractCaseAttribute.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace HalfDog.EasyInteractive
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class InteractCaseAttribute : Attribute
|
||||
{
|
||||
public Type interactSubject;
|
||||
public Type interactTarget;
|
||||
public bool executeOnLoad;
|
||||
|
||||
public InteractCaseAttribute(Type subject, Type target, bool executeOnLoad = true)
|
||||
{
|
||||
interactSubject = subject;
|
||||
interactTarget = target;
|
||||
this.executeOnLoad = executeOnLoad;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user