using System.Collections.Generic;
using UnityEngine.UI;
namespace UnityEngine.EventSystems
{
///
/// Simple event system using physics raycasts.
///
[AddComponentMenu("Event/Physics Raycaster")]
[RequireComponent(typeof(Camera))]
///
/// Raycaster for casting against 3D Physics components.
///
public class PhysicsRaycaster : BaseRaycaster
{
///
/// Const to use for clarity when no event mask is set
///
protected const int kNoEventMaskSet = -1;
protected Camera m_EventCamera;
///
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
///
[SerializeField]
protected LayerMask m_EventMask = kNoEventMaskSet;
///
/// The max number of intersections allowed. 0 = allocating version anything else is non alloc.
///
[SerializeField]
protected int m_MaxRayIntersections = 0;
protected int m_LastMaxRayIntersections = 0;
#if PACKAGE_PHYSICS
RaycastHit[] m_Hits;
#endif
protected PhysicsRaycaster()
{}
public override Camera eventCamera
{
get
{
if (m_EventCamera == null)
m_EventCamera = GetComponent();
return m_EventCamera ?? Camera.main;
}
}
///
/// Depth used to determine the order of event processing.
///
public virtual int depth
{
get { return (eventCamera != null) ? (int)eventCamera.depth : 0xFFFFFF; }
}
///
/// Event mask used to determine which objects will receive events.
///
public int finalEventMask
{
get { return (eventCamera != null) ? eventCamera.cullingMask & m_EventMask : kNoEventMaskSet; }
}
///
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
///
public LayerMask eventMask
{
get { return m_EventMask; }
set { m_EventMask = value; }
}
///
/// Max number of ray intersection allowed to be found.
///
///
/// A value of zero will represent using the allocating version of the raycast function where as any other value will use the non allocating version.
///
public int maxRayIntersections
{
get { return m_MaxRayIntersections; }
set { m_MaxRayIntersections = value; }
}
///
/// Returns a ray going from camera through the event position and the distance between the near and far clipping planes along that ray.
///
/// The pointer event for which we will cast a ray.
/// The ray to use.
/// The display index used.
/// The distance between the near and far clipping planes along the ray.
/// True if the operation was successful. false if it was not possible to compute, such as the eventPosition being outside of the view.
protected bool ComputeRayAndDistance(PointerEventData eventData, ref Ray ray, ref int eventDisplayIndex, ref float distanceToClipPlane)
{
if (eventCamera == null)
return false;
var eventPosition = Display.RelativeMouseAt(eventData.position);
if (eventPosition != Vector3.zero)
{
// We support multiple display and display identification based on event position.
eventDisplayIndex = (int)eventPosition.z;
// Discard events that are not part of this display so the user does not interact with multiple displays at once.
if (eventDisplayIndex != eventCamera.targetDisplay)
return false;
}
else
{
// The multiple display system is not supported on all platforms, when it is not supported the returned position
// will be all zeros so when the returned index is 0 we will default to the event data to be safe.
eventPosition = eventData.position;
}
// Cull ray casts that are outside of the view rect. (case 636595)
if (!eventCamera.pixelRect.Contains(eventPosition))
return false;
ray = eventCamera.ScreenPointToRay(eventPosition);
// compensate far plane distance - see MouseEvents.cs
float projectionDirection = ray.direction.z;
distanceToClipPlane = Mathf.Approximately(0.0f, projectionDirection)
? Mathf.Infinity
: Mathf.Abs((eventCamera.farClipPlane - eventCamera.nearClipPlane) / projectionDirection);
return true;
}
public override void Raycast(PointerEventData eventData, List resultAppendList)
{
#if PACKAGE_PHYSICS
Ray ray = new Ray();
int displayIndex = 0;
float distanceToClipPlane = 0;
if (!ComputeRayAndDistance(eventData, ref ray, ref displayIndex, ref distanceToClipPlane))
return;
int hitCount = 0;
if (m_MaxRayIntersections == 0)
{
if (ReflectionMethodsCache.Singleton.raycast3DAll == null)
return;
m_Hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, finalEventMask);
hitCount = m_Hits.Length;
}
else
{
if (ReflectionMethodsCache.Singleton.getRaycastNonAlloc == null)
return;
if (m_LastMaxRayIntersections != m_MaxRayIntersections)
{
m_Hits = new RaycastHit[m_MaxRayIntersections];
m_LastMaxRayIntersections = m_MaxRayIntersections;
}
hitCount = ReflectionMethodsCache.Singleton.getRaycastNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
}
if (hitCount != 0)
{
if (hitCount > 1)
System.Array.Sort(m_Hits, 0, hitCount, RaycastHitComparer.instance);
for (int b = 0, bmax = hitCount; b < bmax; ++b)
{
var result = new RaycastResult
{
gameObject = m_Hits[b].collider.gameObject,
module = this,
distance = m_Hits[b].distance,
worldPosition = m_Hits[b].point,
worldNormal = m_Hits[b].normal,
screenPosition = eventData.position,
displayIndex = displayIndex,
index = resultAppendList.Count,
sortingLayer = 0,
sortingOrder = 0
};
resultAppendList.Add(result);
}
}
#endif
}
#if PACKAGE_PHYSICS
private class RaycastHitComparer : IComparer
{
public static RaycastHitComparer instance = new RaycastHitComparer();
public int Compare(RaycastHit x, RaycastHit y)
{
return x.distance.CompareTo(y.distance);
}
}
#endif
}
}