using Unity.XR.CoreUtils; using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; using UnityEngine.XR.Interaction.Toolkit.Utilities; namespace UnityEngine.XR.Interaction.Toolkit.AR.Inputs { /// /// Applies the pose computed from the touchscreen gestures to the Transform this component is on. /// Similar to a Tracked Pose Driver in purpose but uses touchscreen gesture input sources to compute /// the pose rather than directly using input actions. /// [AddComponentMenu("XR/Input/Screen Space Ray Pose Driver", 11)] [HelpURL(XRHelpURLConstants.k_ScreenSpaceRayPoseDriver)] [DefaultExecutionOrder(XRInteractionUpdateOrder.k_ScreenSpaceRayPoseDriver)] public class ScreenSpaceRayPoseDriver : MonoBehaviour { [SerializeField] [Tooltip("The camera associated with the screen, and through which screen presses/touches will be interpreted.")] Camera m_ControllerCamera; /// /// The camera associated with the screen, and through which screen presses/touches will be interpreted. /// public Camera controllerCamera { get => m_ControllerCamera; set => m_ControllerCamera = value; } [SerializeField] XRInputValueReader m_TapStartPositionInput = new XRInputValueReader("Tap Start Position"); /// /// Input to use for the screen tap start position. /// /// public XRInputValueReader tapStartPositionInput { get => m_TapStartPositionInput; set => XRInputReaderUtility.SetInputProperty(ref m_TapStartPositionInput, value, this); } [SerializeField] XRInputValueReader m_DragStartPositionInput = new XRInputValueReader("Drag Start Position"); /// /// Input to use for the screen drag start position. /// /// public XRInputValueReader dragStartPositionInput { get => m_DragStartPositionInput; set => XRInputReaderUtility.SetInputProperty(ref m_DragStartPositionInput, value, this); } [SerializeField] XRInputValueReader m_DragCurrentPositionInput = new XRInputValueReader("Drag Current Position"); /// /// Input to use for the screen drag current position. /// /// public XRInputValueReader dragCurrentPositionInput { get => m_DragCurrentPositionInput; set => XRInputReaderUtility.SetInputProperty(ref m_DragCurrentPositionInput, value, this); } [SerializeField] [Tooltip("The input used to read the screen touch count value.")] XRInputValueReader m_ScreenTouchCountInput = new XRInputValueReader("Screen Touch Count"); /// /// The input used to read the screen touch count value. /// /// public XRInputValueReader screenTouchCountInput { get => m_ScreenTouchCountInput; set => XRInputReaderUtility.SetInputProperty(ref m_ScreenTouchCountInput, value, this); } /// /// Holds captured value of the parent transform of this component to avoid doing an Object comparison to null /// each frame when it hasn't changed reference. /// readonly UnityObjectReferenceCache m_ParentTransformCache = new UnityObjectReferenceCache(); Vector2 m_TapStartPosition; Vector2 m_DragStartPosition; /// /// See . /// protected void OnEnable() { if (m_ControllerCamera == null) { m_ControllerCamera = Camera.main; if (m_ControllerCamera == null) { Debug.LogWarning($"Could not find associated {nameof(Camera)} in scene." + $"This {nameof(ScreenSpaceRayPoseDriver)} will be disabled.", this); enabled = false; return; } } m_TapStartPositionInput.EnableDirectActionIfModeUsed(); m_DragStartPositionInput.EnableDirectActionIfModeUsed(); m_DragCurrentPositionInput.EnableDirectActionIfModeUsed(); m_ScreenTouchCountInput.EnableDirectActionIfModeUsed(); } /// /// See . /// protected void OnDisable() { m_TapStartPositionInput.DisableDirectActionIfModeUsed(); m_DragStartPositionInput.DisableDirectActionIfModeUsed(); m_DragCurrentPositionInput.DisableDirectActionIfModeUsed(); m_ScreenTouchCountInput.DisableDirectActionIfModeUsed(); } /// /// See . /// protected void Update() { var prevTapStartPosition = m_TapStartPosition; var tapPerformedThisFrame = m_TapStartPositionInput.TryReadValue(out m_TapStartPosition) && prevTapStartPosition != m_TapStartPosition; var prevDragStartPosition = m_DragStartPosition; var dragStartPerformedThisFrame = m_DragStartPositionInput.TryReadValue(out m_DragStartPosition) && prevDragStartPosition != m_DragStartPosition; if (m_ScreenTouchCountInput.TryReadValue(out var screenTouchCount) && screenTouchCount > 1) return; if (dragStartPerformedThisFrame) { ApplyPose(m_DragStartPosition); return; } if (m_DragCurrentPositionInput.TryReadValue(out var dragCurrentPosition)) { ApplyPose(dragCurrentPosition); return; } if (tapPerformedThisFrame) { ApplyPose(m_TapStartPosition); return; } } void ApplyPose(Vector2 screenPosition) { var screenToWorldPoint = m_ControllerCamera.ScreenToWorldPoint(new Vector3(screenPosition.x, screenPosition.y, m_ControllerCamera.nearClipPlane)); var directionVector = (screenToWorldPoint - m_ControllerCamera.transform.position).normalized; var localPosition = m_ParentTransformCache.TryGet(transform.parent, out var parent) ? parent.InverseTransformPoint(screenToWorldPoint) : screenToWorldPoint; transform.SetLocalPose(new Pose(localPosition, Quaternion.LookRotation(directionVector))); } } }