using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.UI; namespace BNG { public class VRUISystem : BaseInputModule { [Header("XR Controller Options : ")] [Tooltip("This setting determines if LeftPointerTransform or RightPointerTransform will be used as a forward vector for World Space UI events")] public ControllerHand SelectedHand = ControllerHand.Right; [Tooltip("A transform on the left controller to use when raycasting for world space UI events")] public Transform LeftPointerTransform; [Tooltip("A transform on the right controller to use when raycasting for world space UI events")] public Transform RightPointerTransform; [Tooltip("Controller Binding to use for input down, up, etc.")] public List ControllerInput = new List() { ControllerBinding.RightTrigger }; [Tooltip("Unity Input Action used to simulate a click or touch event")] public InputActionReference UIInputAction; [Tooltip("If true a PhysicsRaycaster component will be added to the UI camera, allowing physical objects to use IPointer events such as OnPointClick, OnPointEnter, etc.")] public bool AddPhysicsRaycaster = false; public LayerMask PhysicsRaycasterEventMask; [Tooltip("If true the Right Thumbstick will send scroll events to the UI")] public bool RightThumbstickScroll = true; [Header("Shown for Debug : ")] public GameObject PressingObject; public GameObject DraggingObject; public GameObject ReleasingObject; public PointerEventData EventData { get; private set; } Camera cameraCaster; private GameObject _initialPressObject; private bool _lastInputDown; bool inputDown; private static VRUISystem _instance; public static VRUISystem Instance { get { if (_instance == null) { _instance = GameObject.FindObjectOfType(); if (_instance == null) { // Check for existing event system EventSystem eventSystem = EventSystem.current; if(eventSystem == null) { eventSystem = new GameObject("EventSystem").AddComponent(); ; } _instance = eventSystem.gameObject.AddComponent(); } } return _instance; } } protected override void Awake() { UpdateControllerHand(SelectedHand); EventData = new PointerEventData(eventSystem); EventData.position = new Vector2(cameraCaster.pixelWidth / 2, cameraCaster.pixelHeight / 2); AssignCameraToAllCanvases(cameraCaster); } void init() { if(cameraCaster == null) { // Create the camera required for the caster. // We can reduce the fov and disable the camera component for performance var go = new GameObject("CameraCaster"); cameraCaster = go.AddComponent(); cameraCaster.stereoTargetEye = StereoTargetEyeMask.None; cameraCaster.fieldOfView = 5f; cameraCaster.nearClipPlane = 0.01f; cameraCaster.clearFlags = CameraClearFlags.Nothing; cameraCaster.enabled = false; // Add PhysicsRaycaster so other objects can subscribe to IPointer events if(AddPhysicsRaycaster) { var pr = go.AddComponent(); pr.eventMask = PhysicsRaycasterEventMask; } } } public override void Process() { // Input isn't ready if this Camera Caster's gameObject isn't active if (EventData == null || !CameraCasterReady()) { return; } EventData.position = new Vector2(cameraCaster.pixelWidth / 2, cameraCaster.pixelHeight / 2); eventSystem.RaycastAll(EventData, m_RaycastResultCache); EventData.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache); m_RaycastResultCache.Clear(); // Handle Hover HandlePointerExitAndEnter(EventData, EventData.pointerCurrentRaycast.gameObject); // Handle Drag ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.dragHandler); // Handle scroll if(RightThumbstickScroll) { EventData.scrollDelta = InputBridge.Instance.RightThumbstickAxis; if (!Mathf.Approximately(EventData.scrollDelta.sqrMagnitude, 0)) { ExecuteEvents.Execute(ExecuteEvents.GetEventHandler(EventData.pointerCurrentRaycast.gameObject), EventData, ExecuteEvents.scrollHandler); } } // Press Events inputDown = InputReady(); // On Trigger Down > TriggerDownValue this frame but not last if (inputDown && _lastInputDown == false) { PressDown(); } // On Held Down else if(inputDown) { Press(); } // On Release else { Release(); } _lastInputDown = inputDown; } public virtual bool InputReady() { // Input isn't ready if this Camera Caster's gameObject isn't active if(!CameraCasterReady()) { return false; } // Check Unity Action if (UIInputAction != null && UIInputAction.action.ReadValue() == 1f) { return true; } // Check for bound controller button for (int x = 0; x < ControllerInput.Count; x++) { if (InputBridge.Instance.GetControllerBindingValue(ControllerInput[x])) { return true; } } return false; } /// /// Returns true if we have a camera caster enabled and ready to send out data /// Returns false if the camera caster is null or it's gameobject is disabled /// /// public virtual bool CameraCasterReady() { if (cameraCaster != null && !cameraCaster.gameObject.activeInHierarchy) { return false; } return true; } public virtual void PressDown() { EventData.pointerPressRaycast = EventData.pointerCurrentRaycast; // Deselect if selection changed if(_initialPressObject != null) { // ExecuteEvents.Execute(_initialPressObject, EventData, ExecuteEvents.deselectHandler); _initialPressObject = null; } _initialPressObject = ExecuteEvents.GetEventHandler(EventData.pointerPressRaycast.gameObject); // Set Press Objects and Events SetPressingObject(_initialPressObject); ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerDownHandler); // Set Drag Objects and Events SetDraggingObject(ExecuteEvents.GetEventHandler(EventData.pointerPressRaycast.gameObject)); ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.beginDragHandler); } public virtual void Press() { EventData.pointerPressRaycast = EventData.pointerCurrentRaycast; // Set Press Objects and Events SetPressingObject(ExecuteEvents.GetEventHandler(EventData.pointerPressRaycast.gameObject)); ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerDownHandler); // Set Drag Objects and Events SetDraggingObject(ExecuteEvents.GetEventHandler(EventData.pointerPressRaycast.gameObject)); ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.beginDragHandler); } public virtual void Release() { SetReleasingObject(ExecuteEvents.GetEventHandler(EventData.pointerCurrentRaycast.gameObject)); // Considered a click event if released after an initial click if (EventData.pointerPress == ReleasingObject) { ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerClickHandler); } ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerUpHandler); ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.endDragHandler); // Send deselect to // ExecuteEvents.Execute(ReleasingObject, EventData, ExecuteEvents.deselectHandler); ClearAll(); } public virtual void ClearAll() { SetPressingObject(null); SetDraggingObject(null); EventData.pointerCurrentRaycast.Clear(); } public virtual void SetPressingObject(GameObject pressing) { EventData.pointerPress = pressing; PressingObject = pressing; } public virtual void SetDraggingObject(GameObject dragging) { EventData.pointerDrag = dragging; DraggingObject = dragging; } public virtual void SetReleasingObject(GameObject releasing) { ReleasingObject = releasing; } public virtual void AssignCameraToAllCanvases(Camera cam) { Canvas[] allCanvas = FindObjectsOfType(); for (int x = 0; x < allCanvas.Length; x++) { AddCanvasToCamera(allCanvas[x], cam); } } public virtual void AddCanvas(Canvas canvas) { AddCanvasToCamera(canvas, cameraCaster); } public virtual void AddCanvasToCamera(Canvas canvas, Camera cam) { canvas.worldCamera = cam; } public virtual void UpdateControllerHand(ControllerHand hand) { // Make sure variables exist init(); // Setup the Transform if (hand == ControllerHand.Left && LeftPointerTransform != null) { cameraCaster.transform.parent = LeftPointerTransform; cameraCaster.transform.localPosition = Vector3.zero; cameraCaster.transform.localEulerAngles = Vector3.zero; } else if (hand == ControllerHand.Right && RightPointerTransform != null) { cameraCaster.transform.parent = RightPointerTransform; cameraCaster.transform.localPosition = Vector3.zero; cameraCaster.transform.localEulerAngles = Vector3.zero; } } } }