using System.Collections.Generic; using UnityEngine.EventSystems; using UnityEngine.UI; namespace UnityEngine.UIElements { // This code is disabled unless the com.unity.modules.uielements module is activated in the package manager. #if PACKAGE_UITOOLKIT /// /// A derived BaseRaycaster to raycast against UI Toolkit world-space document instances at runtime. /// [AddComponentMenu("UI Toolkit/World Document Raycaster (UI Toolkit)")] public class WorldDocumentRaycaster : BaseRaycaster { [SerializeField] private Camera m_EventCamera; /// /// The camera that will generate rays for this raycaster. /// public override Camera eventCamera => m_EventCamera; /// /// The camera used by this Raycaster to convert screen coordinates to Rays. /// If empty, Camera.main is going to be used. /// public Camera camera { get => m_EventCamera; set => m_EventCamera = value; } /// /// Raycast against the scene. /// /// Current event data. /// List of hit Objects. public override void Raycast(PointerEventData eventData, List resultAppendList) { var currentInputModule = EventSystem.current != null ? EventSystem.current.currentInputModule : null; if (currentInputModule == null) return; if (!GetWorldRay(eventData, out var worldRay, out var maxDistance, out var layerMask)) return; maxDistance = Mathf.Min(maxDistance, EventSystem.current.uiToolkitInterop.worldPickingMaxDistance); layerMask &= EventSystem.current.uiToolkitInterop.worldPickingLayers; var pointerId = currentInputModule.ConvertUIToolkitPointerId(eventData); // Can be null if runtime panels have not been created or UI Toolkit is stripped; default to null (no capture) var capturingCamera = IRuntimePanel.pointerDeviceState?.GetCameraWithSoftPointerCapture(pointerId); if (capturingCamera != null) { var cam = m_EventCamera != null ? m_EventCamera : Camera.main; if (capturingCamera != cam) return; } var uiElementsUtil = IRuntimePanel.uIElementsRuntimeUtility; if (uiElementsUtil == null || !uiElementsUtil.TryPickWithCapture(pointerId, worldRay, maxDistance, layerMask, out _, out var panelComponent, out var elementUnderPointer, out var distance, out var captured)) return; var containerPanel = panelComponent?.GetContainerPanel(); resultAppendList.Add(new RaycastResult { // Discard hits against non-UI objects. They should block UI but not hide the PhysicsRaycaster results. gameObject = panelComponent == null ? gameObject : containerPanel.selectableGameObject, origin = worldRay.origin, worldPosition = worldRay.origin + distance * worldRay.direction, panelComponent = panelComponent, m_element = elementUnderPointer, module = this, distance = distance, sortingOrder = captured ? int.MaxValue : 0, }); } /// /// Creates a ray from a given pointer event, according to this raycaster's properties. /// Also allows the raycaster to constrain the distance and layers that the ray can use. /// /// Current event data. /// The created ray. /// A distance constraint for the created ray. /// A layer constraint for the created ray. /// Returns true if a valid ray could be created. protected virtual bool GetWorldRay(PointerEventData eventData, out Ray worldRay, out float maxDistance, out int layerMask) { var cam = m_EventCamera != null ? m_EventCamera : Camera.main; if (cam == null) { worldRay = default; maxDistance = 0; layerMask = 0; return false; } maxDistance = cam.farClipPlane; layerMask = cam.cullingMask; Vector3 eventPosition = MultipleDisplayUtilities.GetRelativeMousePositionForRaycast(eventData); // Discard events that are not part of this display so the user does not interact with multiple displays at once. if ((int)eventPosition.z != cam.targetDisplay) { worldRay = default; return false; } worldRay = cam.ScreenPointToRay(eventPosition); return true; } } #endif }