391 lines
12 KiB
C#
391 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
using Studio;
|
|
|
|
namespace Studio.Manage
|
|
{
|
|
[DefaultExecutionOrder(int.MinValue)]
|
|
public class Raycaster : MonoBehaviour
|
|
{
|
|
public enum EventType
|
|
{
|
|
FirstEnter,
|
|
FirstClick,
|
|
FirstExit,
|
|
Enter,
|
|
Click,
|
|
Exit,
|
|
Stay,
|
|
RightClick,
|
|
FirstRightClick,
|
|
FirstStay,
|
|
FirstLeftClickOnly,
|
|
FirstRightClickOnly,
|
|
}
|
|
|
|
PointerEventData pointerEvent = new(EventSystem.current);
|
|
List<RaycastResult> uiRaycastResults = new();
|
|
RaycastHit[] hitInfo = new RaycastHit[100];
|
|
RaycastHit[] tempInfo;
|
|
|
|
bool onLeftClick;
|
|
bool onRightClick;
|
|
Dictionary<Type, RaycastHit> firstHit = new();
|
|
Dictionary<Type, RaycastHit> tempFirstHit = new();
|
|
Dictionary<Transform, RaycastHit> fth = new();
|
|
|
|
int hitCount;
|
|
|
|
Camera cam;
|
|
public RaycastHit hit => hitInfo[0];
|
|
HashSet<Transform> hitTransform = new();
|
|
HashSet<Transform> tempHit = new();
|
|
Dictionary<Transform, RaycastHit> transformToHitinfo = new();
|
|
List<(Action<RaycastHit, Component>, RaycastHit, Component)> eventList = new();
|
|
|
|
public float uiHoverTime;
|
|
float uiHoverTimer;
|
|
#pragma warning disable IDE0044 // 읽기 전용 한정자 추가
|
|
#pragma warning disable CS0649 // 'Raycaster.onUIHoverEvent' 필드에는 할당되지 않으므로 항상 null 기본값을 사용합니다.
|
|
public Action<RaycastResult> onUIHoverEvent;
|
|
public Action onUIHoverExitEvent;
|
|
#pragma warning restore CS0649
|
|
#pragma warning restore IDE0044
|
|
Vector2 prevMousePos;
|
|
|
|
|
|
public override void AfterAwake()
|
|
{
|
|
cam = Camera.main;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
onLeftClick = Input.GetMouseButtonDown(0);
|
|
onRightClick = Input.GetMouseButtonDown(1);
|
|
|
|
if (!UIRaycast())
|
|
{
|
|
PhysicsRaycast();
|
|
}
|
|
EventInvoking();
|
|
}
|
|
|
|
void EventInvoking()
|
|
{
|
|
foreach(var e in eventList)
|
|
{
|
|
e.Item1?.Invoke(e.Item2, e.Item3);
|
|
}
|
|
eventList.Clear();
|
|
}
|
|
bool UIRaycast()
|
|
{
|
|
Vector2 currentMousePos = Input.mousePosition;
|
|
|
|
pointerEvent.position = currentMousePos;
|
|
EventSystem.current.RaycastAll(pointerEvent, uiRaycastResults);
|
|
|
|
uiRaycastResults = uiRaycastResults.Where(r => r.gameObject.GetComponent<Graphic>() != null).ToList();
|
|
|
|
if (uiRaycastResults.Count == 0)
|
|
{
|
|
ResetHoverTime();
|
|
return false;
|
|
}
|
|
|
|
if (onLeftClick)
|
|
{
|
|
|
|
}
|
|
|
|
if (prevMousePos != currentMousePos)
|
|
{
|
|
ResetHoverTime();
|
|
prevMousePos = currentMousePos;
|
|
}
|
|
|
|
if (uiHoverTimer >= uiHoverTime)
|
|
{
|
|
onUIHoverEvent?.Invoke(uiRaycastResults[0]);
|
|
}
|
|
else
|
|
{
|
|
uiHoverTimer += Time.deltaTime;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PhysicsRaycast()
|
|
{
|
|
Rayfire();
|
|
SingleCasting();
|
|
MultiCasting();
|
|
}
|
|
|
|
void Rayfire()
|
|
{
|
|
var ray = cam.ScreenPointToRay(Input.mousePosition);
|
|
//Physics.Raycast(ray, out singleHit, Mathf.Infinity);
|
|
tempInfo = new RaycastHit[100];
|
|
hitCount = Physics.RaycastNonAlloc(ray, tempInfo, Mathf.Infinity);
|
|
hitInfo = SortingHitInfos(tempInfo);
|
|
}
|
|
|
|
public bool Casting(LayerMask layer, out RaycastHit hit)
|
|
{
|
|
hit = new RaycastHit();
|
|
if (hitCount == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach (var h in hitInfo)
|
|
{
|
|
if (h.transform == null)
|
|
continue;
|
|
|
|
if ((layer.value >> h.transform.gameObject.layer) == 1)
|
|
{
|
|
hit = h;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SingleCasting(in RaycastHit hitInfo)
|
|
{
|
|
fth.TryAdd(hitInfo.transform, hitInfo);
|
|
|
|
foreach (var tl in eventTable.Keys)
|
|
{
|
|
SingleCasting(hitInfo, tl);
|
|
}
|
|
}
|
|
|
|
void SingleCasting(in RaycastHit hitInfo, Type tl)
|
|
{
|
|
if (tempFirstHit.ContainsKey(tl))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!hitInfo.transform.TryGetComponent(tl, out var value))
|
|
return;
|
|
|
|
if (firstHit.Remove(tl, out var prev))
|
|
{
|
|
if (prev.transform == hitInfo.transform)
|
|
{
|
|
//Debug.Log($"OnStayFirst : {prev.transform.name}");
|
|
//EventInvoke(onStayFirst, tl, hitInfo, value);
|
|
Invoking(EventType.FirstStay, tl, hitInfo, value);
|
|
}
|
|
else
|
|
{
|
|
//Debug.Log($"OnExitFirst : {prev.transform.name}");
|
|
if (prev.transform.TryGetComponent(tl, out var prevValue))
|
|
{
|
|
//EventInvoke(onExitFirst, tl, prev, prevValue);
|
|
Invoking(EventType.FirstExit, tl, hitInfo, value);
|
|
}
|
|
|
|
//Debug.Log($"OnEnterFirst : {hitInfo.transform.name}");
|
|
//EventInvoke(onEnterFirst, tl, hitInfo, value);
|
|
Invoking(EventType.FirstEnter, tl, hitInfo, value);
|
|
fth.Remove(prev.transform);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Debug.Log($"OnEnterFirst : {hitInfo.transform.name}");
|
|
//EventInvoke(onEnterFirst, tl, hitInfo, value);
|
|
Invoking(EventType.FirstEnter, tl, hitInfo, value);
|
|
}
|
|
fth[hitInfo.transform] = hitInfo;
|
|
tempFirstHit.Add(tl, hitInfo);
|
|
}
|
|
|
|
void SingleCasting()
|
|
{
|
|
//fth.Clear();
|
|
tempFirstHit.Clear();
|
|
for (int i = 0; i < hitCount; ++i)
|
|
{
|
|
SingleCasting(hitInfo[i]);
|
|
}
|
|
|
|
FirstExitEvent();
|
|
FirstClickEvent();
|
|
FirstClickOnlyEvent();
|
|
}
|
|
void FirstClickEvent()
|
|
{
|
|
|
|
foreach (var p in tempFirstHit)
|
|
{
|
|
firstHit.Add(p.Key, p.Value);
|
|
|
|
if (onLeftClick)
|
|
{
|
|
Invoking(EventType.FirstClick, p.Key, p.Value, p.Value.transform.GetComponent(p.Key));
|
|
}
|
|
if (onRightClick)
|
|
{
|
|
Invoking(EventType.FirstRightClick, p.Key, p.Value, p.Value.transform.GetComponent(p.Key));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FirstClickOnlyEvent()
|
|
{
|
|
foreach (var p in tempFirstHit)
|
|
{
|
|
if (onLeftClick)
|
|
{
|
|
Invoking(EventType.FirstLeftClickOnly, p.Key, p.Value, p.Value.transform.GetComponent(p.Key));
|
|
//EventInvoke(onLeftClickOnlyFirst, p.Key, p.Value, p.Value.transform.GetComponent(p.Key));
|
|
return;
|
|
}
|
|
if (onRightClick)
|
|
{
|
|
Invoking(EventType.FirstRightClickOnly, p.Key, p.Value, p.Value.transform.GetComponent(p.Key));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FirstExitEvent()
|
|
{
|
|
foreach (var f in firstHit)
|
|
{
|
|
//Debug.Log($"OnExitFirst :{f.Value.transform.name}");
|
|
if (f.Value.transform == null)
|
|
continue;
|
|
|
|
Invoking(EventType.FirstExit, f.Key, f.Value, f.Value.transform.GetComponent(f.Key));
|
|
fth.Remove(f.Value.transform);
|
|
//tempFirstHit.Remove(f.Key);
|
|
}
|
|
firstHit.Clear();
|
|
}
|
|
|
|
RaycastHit[] SortingHitInfos(in RaycastHit[] hitInfo)
|
|
{
|
|
if (hitInfo[0].transform == null)
|
|
return null;
|
|
|
|
var sortHitInfo = hitInfo.Where(hi => hi.transform != null).OrderBy(hi => hi.distance).ToArray();
|
|
//var sortHitInfo = hitInfo.OrderBy(hi => hi.distance).ToArray();
|
|
|
|
return sortHitInfo;
|
|
}
|
|
|
|
void MultiCasting()
|
|
{
|
|
tempHit.Clear();
|
|
for (int i = 0; i < hitCount; ++i)
|
|
{
|
|
var ht = hitInfo[i].transform;
|
|
tempHit.Add(ht);
|
|
transformToHitinfo.TryAdd(ht, hitInfo[i]);
|
|
bool isStay = hitTransform.Remove(ht);
|
|
|
|
foreach (var tl in eventTable.Keys)
|
|
{
|
|
if (!ht.TryGetComponent(tl, out var value))
|
|
continue;
|
|
|
|
if (onLeftClick)
|
|
{
|
|
Invoking(EventType.Click, tl, hitInfo[i], value);
|
|
//Debug.Log($"OnClick {tl} {value}");
|
|
}
|
|
if(onRightClick)
|
|
{
|
|
Invoking(EventType.RightClick, tl, hitInfo[i], value);
|
|
}
|
|
|
|
if (!isStay)
|
|
{
|
|
//Debug.Log($"OnEnter {tl} {value}");
|
|
Invoking(EventType.Enter, tl, hitInfo[i], value);
|
|
}
|
|
else
|
|
{
|
|
//EventInvoke(onStayEvent_TypeLayer, tl, hitInfo[i], value);
|
|
Invoking(EventType.Stay, tl, hitInfo[i], value);
|
|
//Debug.Log($"OnStay {tl} {value}");
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var h in hitTransform)
|
|
{
|
|
if (h == null)
|
|
continue;
|
|
foreach (var tl in eventTable.Keys)
|
|
{
|
|
if (!h.TryGetComponent(tl, out var value))
|
|
continue;
|
|
Invoking(EventType.Exit, tl, transformToHitinfo[h], value);
|
|
//EventInvoke(onExitEvent_TypeLayer, tl, transformToHitinfo[h], value);
|
|
//Debug.Log($"OnExit {tl} {value}");
|
|
}
|
|
transformToHitinfo.Remove(h);
|
|
}
|
|
|
|
hitTransform.Clear();
|
|
foreach (var p in tempHit)
|
|
{
|
|
hitTransform.Add(p);
|
|
}
|
|
}
|
|
|
|
void Invoking(EventType et, Type t, in RaycastHit hitInfo, Component value)
|
|
{
|
|
if(eventTable.TryGetValue(t, out var actionTable))
|
|
{
|
|
if(actionTable.TryGetValue(et, out var action))
|
|
{
|
|
eventList.Add((action, hitInfo, value));
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsFirstHit(Transform target)
|
|
{
|
|
if (hitInfo.Length == 0)
|
|
return false;
|
|
return hitInfo[0].transform == target;
|
|
}
|
|
|
|
Dictionary<Type, Dictionary<EventType, Action<RaycastHit, Component>>> eventTable = new();
|
|
public void AddEvent(EventType et, Type layer, Action<RaycastHit, Component> action)
|
|
{
|
|
if (!eventTable.ContainsKey(layer))
|
|
eventTable.Add(layer, new Dictionary<EventType, Action<RaycastHit, Component>>());
|
|
|
|
if (!eventTable[layer].ContainsKey(et))
|
|
eventTable[layer].Add(et, null);
|
|
|
|
eventTable[layer][et] += action;
|
|
}
|
|
|
|
public void RemoveEvent(EventType et, Type layer, Action<RaycastHit, Component> action)
|
|
{
|
|
eventTable[layer][et]-=action;
|
|
}
|
|
|
|
void ResetHoverTime()
|
|
{
|
|
onUIHoverExitEvent?.Invoke();
|
|
uiHoverTimer = 0f;
|
|
}
|
|
}
|
|
} |