Files
ChunilENG/Assets/Plugins/XRLib/GenericController/Raycaster.cs

402 lines
12 KiB
C#
Raw Normal View History

2025-02-20 09:59:37 +09:00
using System;
using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor.Presets;
#endif
using UnityEngine;
using UnityEngine.EventSystems;
namespace WI
{
[DefaultExecutionOrder(int.MinValue)]
public class Raycaster : MonoBehaviour, ISingle
{
PointerEventData pointerEvent = new(EventSystem.current);
List<RaycastResult> uiRaycastResults = new();
RaycastHit[] hitInfo = new RaycastHit[16];
RaycastHit[] tempInfo;
HashSet<Type> typeLayers = new();
Dictionary<Type, Action<RaycastHit, Component>> onExitEvent = new();
Dictionary<Type, Action<RaycastHit, Component>> onStayEvent = new();
Dictionary<Type, Action<RaycastHit, Component>> onEnterEvent = new();
Dictionary<Type, Action<RaycastHit, Component>> onLeftClickEvent = new();
Dictionary<Type, Action<RaycastHit, Component>> onRightClickEvent = new();
Dictionary<Type, Action<RaycastHit, Component>> onLeftClickFirst = new();
Dictionary<Type, Action<RaycastHit, Component>> onRightClickFirst = new();
Dictionary<Type, Action<RaycastHit, Component>> onEnterFirst = new();
Dictionary<Type, Action<RaycastHit, Component>> onStayFirst = new();
Dictionary<Type, Action<RaycastHit, Component>> onExitFirst = new();
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 기본값을 사용합니다.
Action<RaycastResult> onUIHoverEvent;
#pragma warning restore CS0649
#pragma warning restore IDE0044
GameObject prevOnUI;
void Awake()
{
cam = Camera.main;
}
void Update()
{
UIRaycast();
PhysicsRaycast();
EventInvoking();
}
void EventInvoking()
{
foreach(var e in eventList)
{
e.Item1.Invoke(e.Item2, e.Item3);
}
eventList.Clear();
}
void UIRaycast()
{
pointerEvent.position = Input.mousePosition;
EventSystem.current.RaycastAll(pointerEvent, uiRaycastResults);
if(uiRaycastResults.Count != 0)
{
if (uiRaycastResults[0].gameObject == null)
{
return;
}
if (prevOnUI != uiRaycastResults[0].gameObject)
{
uiHoverTimer = 0f;
prevOnUI = uiRaycastResults[0].gameObject;
}
if (uiHoverTimer >= uiHoverTime)
{
onUIHoverEvent?.Invoke(uiRaycastResults[0]);
}
else
{
uiHoverTimer += Time.deltaTime;
}
}
else
{
prevOnUI = null;
}
}
void PhysicsRaycast()
{
onLeftClick = Input.GetMouseButtonDown(0);
onRightClick = Input.GetMouseButtonDown(1);
Rayfire();
SingleCasting();
MultiCasting();
}
void Rayfire()
{
var ray = cam.ScreenPointToRay(Input.mousePosition);
//Physics.Raycast(ray, out singleHit, Mathf.Infinity);
tempInfo = new RaycastHit[16];
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 typeLayers)
{
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);
}
else
{
//Debug.Log($"OnExitFirst : {prev.transform.name}");
EventInvoke(onExitFirst, tl, prev, prev.transform.GetComponent(tl));
//Debug.Log($"OnEnterFirst : {hitInfo.transform.name}");
EventInvoke(onEnterFirst, tl, hitInfo, value);
fth.Remove(prev.transform);
}
}
else
{
//Debug.Log($"OnEnterFirst : {hitInfo.transform.name}");
EventInvoke(onEnterFirst, 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();
}
void FirstClickEvent()
{
foreach (var p in tempFirstHit)
{
firstHit.Add(p.Key, p.Value);
if (onLeftClick)
{
EventInvoke(onLeftClickFirst, p.Key, p.Value, p.Value.transform.GetComponent(p.Key));
}
if (onRightClick)
{
EventInvoke(onRightClickFirst, p.Key, p.Value, p.Value.transform.GetComponent(p.Key));
}
}
}
void FirstExitEvent()
{
foreach (var f in firstHit)
{
//Debug.Log($"OnExitFirst :{f.Value.transform.name}");
if (f.Value.transform == null)
continue;
EventInvoke(onExitFirst, 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 HitCheck(int hitIndex)
{
var ht = hitInfo[hitIndex].transform;
tempHit.Add(ht);
transformToHitinfo.TryAdd(ht, hitInfo[hitIndex]);
bool isStay = hitTransform.Remove(ht);
foreach (var tl in typeLayers)
{
if (!ht.TryGetComponent(tl, out var value))
continue;
if (onLeftClick)
{
EventInvoke(onLeftClickEvent, tl, hitInfo[hitIndex], value);
//Debug.Log($"OnClick {tl} {value}");
}
if (onRightClick)
{
EventInvoke(onRightClickEvent, tl, hitInfo[hitIndex], value);
}
if (!isStay)
{
//Debug.Log($"OnEnter {tl} {value}");
EventInvoke(onEnterEvent, tl, hitInfo[hitIndex], value);
}
else
{
EventInvoke(onStayEvent, tl, hitInfo[hitIndex], value);
//Debug.Log($"OnStay {tl} {value}");
}
}
}
void MultiCasting()
{
tempHit.Clear();
for (int i = 0; i < hitCount; ++i)
{
HitCheck(i);
}
foreach (var h in hitTransform)
{
if (h == null)
continue;
foreach (var tl in typeLayers)
{
if (!h.TryGetComponent(tl, out var value))
continue;
EventInvoke(onExitEvent, tl, transformToHitinfo[h], value);
}
transformToHitinfo.Remove(h);
}
hitTransform.Clear();
foreach (var p in tempHit)
{
hitTransform.Add(p);
}
}
void EventInvoke(Dictionary<Type, Action<RaycastHit, Component>> eventTable, Type layer, in RaycastHit hitInfo, Component value)
{
if (eventTable.TryGetValue(layer, out var action))
{
eventList.Add((action,hitInfo, value));
//action?.Invoke(hitInfo, value);
}
}
public bool IsFirstHit(Transform target)
{
if (hitInfo.Length == 0)
return false;
return hitInfo[0].transform == target;
}
public void AddTypeLayer(Type t)
{
typeLayers.Add(t);
typeLayers.RemoveWhere(t => t == null);
}
public void RemoveTypeLayer<T>()
{
typeLayers.Remove(typeof(T));
}
public void AddEvent_FirstEnter(Type layer, Action<RaycastHit, Component> action)
{
onEnterFirst.TryAdd(layer, null);
onEnterFirst[layer] += action;
}
public void AddEvent_FirstExit(Type layer, Action<RaycastHit, Component> action)
{
onExitFirst.TryAdd(layer, null);
onExitFirst[layer] += action;
}
public void AddEvent_FirstStay(Type layer, Action<RaycastHit, Component> action)
{
onStayFirst.TryAdd(layer, null);
onStayFirst[layer] += action;
}
public void AddEvent_FirstLeftClick(Type layer, Action<RaycastHit, Component> action)
{
onLeftClickFirst.TryAdd(layer, null);
onLeftClickFirst[layer] += action;
}
public void AddEvent_FirstRightClick(Type layer, Action<RaycastHit, Component> action)
{
onRightClickFirst.TryAdd(layer, null);
onRightClickFirst[layer] += action;
}
public void AddEvent_RightClick(Type layer, Action<RaycastHit, Component> action)
{
onRightClickEvent.TryAdd(layer, null);
onRightClickEvent[layer] += action;
}
public void AddEvent_Enter(Type layer, Action<RaycastHit, Component> action)
{
onEnterEvent.TryAdd(layer, null);
onEnterEvent[layer] += action;
}
public void AddEvent_Exit(Type layer, Action<RaycastHit, Component> action)
{
onExitEvent.TryAdd(layer, null);
onExitEvent[layer] += action;
}
public void AddEvent_Stay(Type layer, Action<RaycastHit, Component> action)
{
onStayEvent.TryAdd(layer, null);
onStayEvent[layer] += action;
}
public void AddEvent_LeftClick(Type layer, Action<RaycastHit, Component> action)
{
onLeftClickEvent.TryAdd(layer, null);
onLeftClickEvent[layer] += action;
}
}
}