| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- using System;
- using System.Collections.Generic;
- using System.Text;
- using UnityEngine.EventSystems;
- using UnityEngine.Serialization;
- namespace UnityEngine.UI
- {
- [AddComponentMenu("Event/Graphic Raycaster")]
- [RequireComponent(typeof(Canvas))]
- /// <summary>
- /// A derived BaseRaycaster to raycast against Graphic elements.
- /// </summary>
- public class GraphicRaycaster : BaseRaycaster
- {
- protected const int kNoEventMaskSet = -1;
- /// <summary>
- /// Type of raycasters to check against to check for canvas blocking elements.
- /// </summary>
- public enum BlockingObjects
- {
- /// <summary>
- /// Perform no raycasts.
- /// </summary>
- None = 0,
- /// <summary>
- /// Perform a 2D raycast check to check for blocking 2D elements
- /// </summary>
- TwoD = 1,
- /// <summary>
- /// Perform a 3D raycast check to check for blocking 3D elements
- /// </summary>
- ThreeD = 2,
- /// <summary>
- /// Perform a 2D and a 3D raycasts to check for blocking 2D and 3D elements.
- /// </summary>
- All = 3,
- }
- /// <summary>
- /// Priority of the raycaster based upon sort order.
- /// </summary>
- /// <returns>
- /// The sortOrder priority.
- /// </returns>
- public override int sortOrderPriority
- {
- get
- {
- // We need to return the sorting order here as distance will all be 0 for overlay.
- if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
- return canvas.sortingOrder;
- return base.sortOrderPriority;
- }
- }
- /// <summary>
- /// Priority of the raycaster based upon render order.
- /// </summary>
- /// <returns>
- /// The renderOrder priority.
- /// </returns>
- public override int renderOrderPriority
- {
- get
- {
- // We need to return the sorting order here as distance will all be 0 for overlay.
- if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
- return canvas.rootCanvas.renderOrder;
- return base.renderOrderPriority;
- }
- }
- [FormerlySerializedAs("ignoreReversedGraphics")]
- [SerializeField]
- private bool m_IgnoreReversedGraphics = true;
- [FormerlySerializedAs("blockingObjects")]
- [SerializeField]
- private BlockingObjects m_BlockingObjects = BlockingObjects.None;
- /// <summary>
- /// Whether Graphics facing away from the raycaster are checked for raycasts.
- /// </summary>
- public bool ignoreReversedGraphics { get {return m_IgnoreReversedGraphics; } set { m_IgnoreReversedGraphics = value; } }
- /// <summary>
- /// The type of objects that are checked to determine if they block graphic raycasts.
- /// </summary>
- public BlockingObjects blockingObjects { get {return m_BlockingObjects; } set { m_BlockingObjects = value; } }
- [SerializeField]
- protected LayerMask m_BlockingMask = kNoEventMaskSet;
- /// <summary>
- /// The type of objects specified through LayerMask that are checked to determine if they block graphic raycasts.
- /// </summary>
- public LayerMask blockingMask { get { return m_BlockingMask; } set { m_BlockingMask = value; } }
- private Canvas m_Canvas;
- protected GraphicRaycaster()
- {}
- private Canvas canvas
- {
- get
- {
- if (m_Canvas != null)
- return m_Canvas;
- m_Canvas = GetComponent<Canvas>();
- return m_Canvas;
- }
- }
- [NonSerialized] private List<Graphic> m_RaycastResults = new List<Graphic>();
- /// <summary>
- /// Perform the raycast against the list of graphics associated with the Canvas.
- /// </summary>
- /// <param name="eventData">Current event data</param>
- /// <param name="resultAppendList">List of hit objects to append new results to.</param>
- public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
- {
- if (canvas == null)
- return;
- var canvasGraphics = GraphicRegistry.GetRaycastableGraphicsForCanvas(canvas);
- if (canvasGraphics == null || canvasGraphics.Count == 0)
- return;
- int displayIndex;
- var currentEventCamera = eventCamera; // Property can call Camera.main, so cache the reference
- if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null)
- displayIndex = canvas.targetDisplay;
- else
- displayIndex = currentEventCamera.targetDisplay;
- var eventPosition = Display.RelativeMouseAt(eventData.position);
- if (eventPosition != Vector3.zero)
- {
- // We support multiple display and display identification based on event position.
- int 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 != displayIndex)
- return;
- }
- 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;
- // We dont really know in which display the event occured. We will process the event assuming it occured in our display.
- }
- // Convert to view space
- Vector2 pos;
- if (currentEventCamera == null)
- {
- // Multiple display support only when not the main display. For display 0 the reported
- // resolution is always the desktops resolution since its part of the display API,
- // so we use the standard none multiple display method. (case 741751)
- float w = Screen.width;
- float h = Screen.height;
- if (displayIndex > 0 && displayIndex < Display.displays.Length)
- {
- w = Display.displays[displayIndex].systemWidth;
- h = Display.displays[displayIndex].systemHeight;
- }
- pos = new Vector2(eventPosition.x / w, eventPosition.y / h);
- }
- else
- pos = currentEventCamera.ScreenToViewportPoint(eventPosition);
- // If it's outside the camera's viewport, do nothing
- if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f)
- return;
- float hitDistance = float.MaxValue;
- Ray ray = new Ray();
- if (currentEventCamera != null)
- ray = currentEventCamera.ScreenPointToRay(eventPosition);
- if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)
- {
- float distanceToClipPlane = 100.0f;
- if (currentEventCamera != null)
- {
- float projectionDirection = ray.direction.z;
- distanceToClipPlane = Mathf.Approximately(0.0f, projectionDirection)
- ? Mathf.Infinity
- : Mathf.Abs((currentEventCamera.farClipPlane - currentEventCamera.nearClipPlane) / projectionDirection);
- }
- #if PACKAGE_PHYSICS
- if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
- {
- if (ReflectionMethodsCache.Singleton.raycast3D != null)
- {
- var hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, (int)m_BlockingMask);
- if (hits.Length > 0)
- hitDistance = hits[0].distance;
- }
- }
- #endif
- #if PACKAGE_PHYSICS2D
- if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
- {
- if (ReflectionMethodsCache.Singleton.raycast2D != null)
- {
- var hits = ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, (int)m_BlockingMask);
- if (hits.Length > 0)
- hitDistance = hits[0].distance;
- }
- }
- #endif
- }
- m_RaycastResults.Clear();
- Raycast(canvas, currentEventCamera, eventPosition, canvasGraphics, m_RaycastResults);
- int totalCount = m_RaycastResults.Count;
- for (var index = 0; index < totalCount; index++)
- {
- var go = m_RaycastResults[index].gameObject;
- bool appendGraphic = true;
- if (ignoreReversedGraphics)
- {
- if (currentEventCamera == null)
- {
- // If we dont have a camera we know that we should always be facing forward
- var dir = go.transform.rotation * Vector3.forward;
- appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
- }
- else
- {
- // If we have a camera compare the direction against the cameras forward.
- var cameraForward = currentEventCamera.transform.rotation * Vector3.forward * currentEventCamera.nearClipPlane;
- appendGraphic = Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward, go.transform.forward) >= 0;
- }
- }
- if (appendGraphic)
- {
- float distance = 0;
- Transform trans = go.transform;
- Vector3 transForward = trans.forward;
- if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
- distance = 0;
- else
- {
- // http://geomalgorithms.com/a06-_intersect-2.html
- distance = (Vector3.Dot(transForward, trans.position - ray.origin) / Vector3.Dot(transForward, ray.direction));
- // Check to see if the go is behind the camera.
- if (distance < 0)
- continue;
- }
- if (distance >= hitDistance)
- continue;
- var castResult = new RaycastResult
- {
- gameObject = go,
- module = this,
- distance = distance,
- screenPosition = eventPosition,
- displayIndex = displayIndex,
- index = resultAppendList.Count,
- depth = m_RaycastResults[index].depth,
- sortingLayer = canvas.sortingLayerID,
- sortingOrder = canvas.sortingOrder,
- worldPosition = ray.origin + ray.direction * distance,
- worldNormal = -transForward
- };
- resultAppendList.Add(castResult);
- }
- }
- }
- /// <summary>
- /// The camera that will generate rays for this raycaster.
- /// </summary>
- /// <returns>
- /// - Null if Camera mode is ScreenSpaceOverlay or ScreenSpaceCamera and has no camera.
- /// - canvas.worldCanvas if not null
- /// - Camera.main.
- /// </returns>
- public override Camera eventCamera
- {
- get
- {
- var canvas = this.canvas;
- var renderMode = canvas.renderMode;
- if (renderMode == RenderMode.ScreenSpaceOverlay
- || (renderMode == RenderMode.ScreenSpaceCamera && canvas.worldCamera == null))
- return null;
- return canvas.worldCamera ?? Camera.main;
- }
- }
- /// <summary>
- /// Perform a raycast into the screen and collect all graphics underneath it.
- /// </summary>
- [NonSerialized] static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
- private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
- {
- // Necessary for the event system
- int totalCount = foundGraphics.Count;
- for (int i = 0; i < totalCount; ++i)
- {
- Graphic graphic = foundGraphics[i];
- // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
- if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
- continue;
- if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera, graphic.raycastPadding))
- continue;
- if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
- continue;
- if (graphic.Raycast(pointerPosition, eventCamera))
- {
- s_SortedGraphics.Add(graphic);
- }
- }
- s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
- totalCount = s_SortedGraphics.Count;
- for (int i = 0; i < totalCount; ++i)
- results.Add(s_SortedGraphics[i]);
- s_SortedGraphics.Clear();
- }
- }
- }
|