using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; #if UNITY_2019_3_OR_NEWER using UnityEngine.XR; #endif #if ZED_STEAM_VR using Valve.VR; #endif /// /// Extended version of ZEDControllerTracker that also checks for several inputs in a generic way. /// You can check a state with /// Used because input methods vary a lot between controllers and between SteamVR (new and old) and Oculus. /// See base class ZEDControllerTracker for any code that don't directly relate to inputs. /// public class ZEDControllerTracker_DemoInputs : ZEDControllerTracker { //#if ZED_STEAM_VR #if ZED_SVR_2_0_INPUT /// !! On v2.0, Steam VR action bindings must be done in the inspector ro once steam.initialize(true) has been called !! /// /// SteamVR action to cause a Fire event when checked or subscribed to. /// [Header("SteamVR Plugin 2.0 Bindings")] [Tooltip("SteamVR action to cause a Fire event when checked or subscribed to.")] public SteamVR_Action_Boolean fireBinding;// = SteamVR_Input.GetAction("Fire"); /// /// SteamVR action to cause a Click event when checked or subscribed to. /// [Tooltip("SteamVR action to cause a Click event when checked or subscribed to.")] public SteamVR_Action_Boolean clickBinding;// = SteamVR_Input.GetAction("Click"); /// /// SteamVR action to cause a Back event when checked or subscribed to. /// [Tooltip("SteamVR action to cause a Back event when checked or subscribed to.")] public SteamVR_Action_Boolean backBinding;// = SteamVR_Input.GetAction("Back"); /// /// SteamVR action to cause a Grab event when checked or subscribed to. /// [Tooltip("SteamVR action to cause a Grab event when checked or subscribed to.")] public SteamVR_Action_Boolean grabBinding;// = SteamVR_Input.GetAction("Grab"); /// /// SteamVR action to cause a Vector2 UI navigation event when checked or subscribed to. /// [Tooltip("SteamVR action to cause a UI navigation event when checked or subscribed to.")] public SteamVR_Action_Vector2 navigateUIBinding;// = SteamVR_Input.GetAction("NavigateUI"); #elif ZED_STEAM_VR /// /// Legacy SteamVR button to cause a Fire event when checked or subscribed to. /// [Header("SteamVR Legacy Input Bindings")] [Tooltip("Legacy SteamVR button to cause a Fire event when checked or subscribed to.")] public EVRButtonId fireBinding_Legacy = EVRButtonId.k_EButton_SteamVR_Trigger; /// /// Legacy SteamVR button to cause a Click event when checked or subscribed to. /// [Tooltip("Legacy SteamVR button to cause a Click event when checked or subscribed to.")] public EVRButtonId clickBinding_Legacy = EVRButtonId.k_EButton_SteamVR_Trigger; /// /// Legacy SteamVR button to cause a Back event when checked or subscribed to. /// [Tooltip("Legacy SteamVR button to cause a Back event when checked or subscribed to.")] public EVRButtonId backBinding_Legacy = EVRButtonId.k_EButton_Grip; /// /// Legacy SteamVR button to cause a Grip event when checked or subscribed to. /// [Tooltip("Legacy SteamVR button to cause a Grip event when checked or subscribed to.")] public EVRButtonId grabBinding_Legacy = EVRButtonId.k_EButton_SteamVR_Trigger; /// /// Legacy SteamVR axis to cause a Vector2 Navigate UI event when checked or subscribed to. /// [Tooltip("Legacy SteamVR button to cause a Vector2 Navigate UI event when checked or subscribed to.")] public EVRButtonId navigateUIBinding_Legacy = EVRButtonId.k_EButton_Axis0; #endif #if ZED_OCULUS public static bool ovrUpdateCalledThisFrame = false; #if UNITY_2019_3_OR_NEWER /// /// Input Button checked to signal a Fire event when checked or subscribed to. /// [Header("Input Bindings")] [Tooltip("Input Button checked to signal a Fire event when checked or subscribed to")] public InputFeatureUsage fireButton = CommonUsages.triggerButton; /// /// Input Button checked to signal a Click event when checked or subscribed to. /// [Tooltip("Input Button checked to signal a Click event when checked or subscribed to")] public InputFeatureUsage clickButton = CommonUsages.triggerButton; /// /// Input Button checked to signal a Back event when checked or subscribed to. /// [Tooltip("Input Button checked to signal a Back event when checked or subscribed to")] public InputFeatureUsage backButton = CommonUsages.secondaryButton; //Y, or B if just right controller is connected. /// /// Input Button checked to signal a Grab event when checked or subscribed to. /// [Tooltip("Input Button checked to signal a Grab event when checked or subscribed to")] public InputFeatureUsage grabButton = CommonUsages.gripButton; /// /// Input Button checked to signal a Vector2 UI navigation event when checked or subscribed to. /// [Tooltip("Input Button checked to signal a Vector2 UI navigation event when checked or subscribed to")] public InputFeatureUsage navigateUIAxis = CommonUsages.primary2DAxis; private bool fireActive = false; private bool clickActive = false; private bool backActive = false; private bool grabActive = false; #else /// /// Oculus Button checked to signal a Fire event when checked or subscribed to. /// [Header("Oculus Input Bindings")] [Tooltip("Oculus Button checked to signal a Fire event when checked or subscribed to")] public OVRInput.Button fireButton = OVRInput.Button.PrimaryIndexTrigger; /// /// Oculus Button checked to signal a Click event when checked or subscribed to. /// [Tooltip("Oculus Button checked to signal a Click event when checked or subscribed to")] public OVRInput.Button clickButton = OVRInput.Button.PrimaryIndexTrigger; /// /// Oculus Button checked to signal a Back event when checked or subscribed to. /// [Tooltip("Oculus Button checked to signal a Back event when checked or subscribed to")] public OVRInput.Button backButton = OVRInput.Button.Two; //Y, or B if just right controller is connected. /// /// Oculus Button checked to signal a Grab event when checked or subscribed to. /// [Tooltip("Oculus Button checked to signal a Grab event when checked or subscribed to")] public OVRInput.Button grabButton = OVRInput.Button.PrimaryHandTrigger; /// /// Oculus Button checked to signal a Vector2 UI navigation event when checked or subscribed to. /// [Tooltip("Oculus Button checked to signal a Vector2 UI navigation event when checked or subscribed to")] public OVRInput.Axis2D navigateUIAxis = OVRInput.Axis2D.PrimaryThumbstick; #endif #endif /// /// Events called when the Fire button/action was just pressed. /// [Header("Events")] [Space(5)] [Tooltip("Events called when the Fire button/action was just pressed.")] public UnityEvent onFireDown; /// /// Events called when the Fire button/action was just released. /// [Tooltip("Events called when the Fire button/action was just released.")] public UnityEvent onFireUp; /// /// Events called when the Click button/action was just pressed. /// [Tooltip("Events called when the Click button/action was just pressed.")] public UnityEvent onClickDown; /// /// Events called when the Click button/action was just released. /// [Tooltip("Events called when the Click button/action was just released.")] public UnityEvent onClickUp; /// /// Events called when the Back button/action was just pressed. /// [Tooltip("Events called when the Back button/action was just pressed.")] public UnityEvent onBackDown; /// /// Events called when the Back button/action was just released. /// [Tooltip("Events called when the Back button/action was just released.")] public UnityEvent onBackUp; /// /// Events called when the Grab button/action was just pressed. /// [Tooltip("Events called when the Grab button/action was just pressed.")] public UnityEvent onGrabDown; /// /// Events called when the Grab button/action was just released. /// [Tooltip("Events called when the Grab button/action was just released.")] public UnityEvent onGrabUp; /// /// Returns if the Fire button/action matched the provided state. /// /// Whether to check if the button/action is just pressed, just released, or is being held down. public bool CheckFireButton(ControllerButtonState state) { #if ZED_SVR_2_0_INPUT return CheckSteamVRBoolActionState(fireBinding, state); #elif ZED_STEAM_VR return CheckSteamVRButtonState_Legacy(fireBinding_Legacy, state); #endif #if ZED_OCULUS #if UNITY_2019_3_OR_NEWER return CheckButtonState(fireButton, state, fireActive); #else return CheckOculusButtonState(fireButton, state); #endif #endif return false; } /// /// Returns if the Click button/action matched the provided state. /// /// Whether to check if the button/action is just pressed, just released, or is being held down. public bool CheckClickButton(ControllerButtonState state) { #if ZED_SVR_2_0_INPUT return CheckSteamVRBoolActionState(clickBinding, state); #elif ZED_STEAM_VR return CheckSteamVRButtonState_Legacy(clickBinding_Legacy, state); #endif #if ZED_OCULUS #if UNITY_2019_3_OR_NEWER return CheckButtonState(clickButton, state, clickActive); #else return CheckOculusButtonState(clickButton, state); #endif #endif return false; } /// /// Returns if the Back button/action matched the provided state. /// /// Whether to check if the button/action is just pressed, just released, or is being held down. public bool CheckBackButton(ControllerButtonState state) { #if ZED_SVR_2_0_INPUT return CheckSteamVRBoolActionState(backBinding, state); #elif ZED_STEAM_VR return CheckSteamVRButtonState_Legacy(backBinding_Legacy, state); #endif #if ZED_OCULUS #if UNITY_2019_3_OR_NEWER return CheckButtonState(backButton, state, backActive); #else return CheckOculusButtonState(backButton, state); #endif #endif return false; } /// /// Returns if the Grab button/action matched the provided state. /// /// Whether to check if the button/action is just pressed, just released, or is being held down. public bool CheckGrabButton(ControllerButtonState state) { #if ZED_SVR_2_0_INPUT return CheckSteamVRBoolActionState(grabBinding, state); #elif ZED_STEAM_VR return CheckSteamVRButtonState_Legacy(grabBinding_Legacy, state); #endif #if ZED_OCULUS #if UNITY_2019_3_OR_NEWER return CheckButtonState(grabButton, state, grabActive); #else return CheckOculusButtonState(grabButton, state); #endif #endif return false; } /// /// Returns the current 2D axis value of the NavigateUIAxis button/action. /// public Vector2 CheckNavigateUIAxis() { #if ZED_SVR_2_0_INPUT return CheckSteamVR2DAxis(navigateUIBinding); #elif ZED_STEAM_VR return CheckSteamVRAxis_Legacy(navigateUIBinding_Legacy); #endif #if ZED_OCULUS #if UNITY_2019_3_OR_NEWER return Check2DAxisState(navigateUIAxis); #else return CheckOculus2DAxisState(navigateUIAxis); #endif #endif return Vector3.zero; } protected override void Awake() { base.Awake(); #if ZED_SVR_2_0_INPUT if (!useLegacySteamVRInput) { if(!SteamVR.active) SteamVR.Initialize(true); //Force SteamVR to activate, so we can use the input system. //script binding example //fireBinding = SteamVR_Input._default.inActions.GrabGrip; //... } #endif } protected override void Update() { base.Update(); if (CheckClickButton(ControllerButtonState.Down)) onClickDown.Invoke(); if (CheckClickButton(ControllerButtonState.Up)) onClickUp.Invoke(); if (CheckFireButton(ControllerButtonState.Down)) onFireDown.Invoke(); if (CheckFireButton(ControllerButtonState.Up)) onFireUp.Invoke(); if (CheckBackButton(ControllerButtonState.Down)) onBackDown.Invoke(); if (CheckBackButton(ControllerButtonState.Up)) onBackUp.Invoke(); if (CheckGrabButton(ControllerButtonState.Down)) onGrabDown.Invoke(); if (CheckGrabButton(ControllerButtonState.Up)) onGrabUp.Invoke(); } protected void LateUpdate() { #if ZED_OCULUS ovrUpdateCalledThisFrame = false; #endif } #if ZED_STEAM_VR protected override void UpdateControllerState() { base.UpdateControllerState(); //If using legacy SteamVR input, we check buttons directly from the OpenVR API. #if ZED_SVR_2_0_INPUT //If using SteamVR plugin 2.0 or higher, give the option to use legacy input. if (useLegacySteamVRInput) { openvrsystem.GetControllerState((uint)index, ref controllerstate, controllerstatesize); } #else //We're using an older SteamVR plugin, so we need to use the legacy input. openvrsystem.GetControllerState((uint)index, ref controllerstate, controllerstatesize); #endif } #endif #if ZED_OCULUS /// /// Checks the button state of a given Oculus button. /// /// Whether to check if the button/action is just pressed, just released, or is being held down. public bool CheckOculusButtonState(OVRInput.Button button, ControllerButtonState state) { if (!ovrUpdateCalledThisFrame) { OVRInput.Update(); ovrUpdateCalledThisFrame = true; } bool result = false; switch (state) { case ControllerButtonState.Down: result = OVRInput.GetDown(button, GetOculusController()); break; case ControllerButtonState.Held: result = OVRInput.Get(button, GetOculusController()); break; case ControllerButtonState.Up: result = OVRInput.GetUp(button, GetOculusController()); break; } return result; } #if UNITY_2019_3_OR_NEWER public bool CheckButtonState(InputFeatureUsage button, ControllerButtonState state, bool isActive){ bool down = false; bool up = false; InputDevice device = new InputDevice(); if (deviceToTrack == Devices.LeftController) device = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand); else device = InputDevices.GetDeviceAtXRNode(XRNode.RightHand); ProcessInputDeviceButton(device, button, ref isActive, () => // On Button Down { down = true; }, () => // On Button Up { up = true; }); if (state == ControllerButtonState.Down) return down; if (state == ControllerButtonState.Up) return up; else return false; } public Vector2 Check2DAxisState(InputFeatureUsage navigateUIAxis){ InputDevice device = new InputDevice(); if (deviceToTrack == Devices.LeftController) device = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand); else device = InputDevices.GetDeviceAtXRNode(XRNode.RightHand); Vector2 result = Vector2.zero; if (device.TryGetFeatureValue(navigateUIAxis, out Vector2 value)) result = value; return result; } private void ProcessInputDeviceButton(InputDevice inputDevice, InputFeatureUsage button, ref bool _wasPressedDownPreviousFrame, Action onButtonDown = null, Action onButtonUp = null, Action onButtonHeld = null) { if (inputDevice.TryGetFeatureValue(button, out bool isPressed) && isPressed) { if (!_wasPressedDownPreviousFrame) // // this is button down { onButtonDown?.Invoke(); } _wasPressedDownPreviousFrame = true; onButtonHeld?.Invoke(); } else { if (_wasPressedDownPreviousFrame) // this is button up { onButtonUp?.Invoke(); } _wasPressedDownPreviousFrame = false; } } #endif /// /// Returns the axis of a given Oculus axis button/joystick. /// public Vector3 CheckOculus2DAxisState(OVRInput.Axis2D axis) { if (!ovrUpdateCalledThisFrame) { OVRInput.Update(); ovrUpdateCalledThisFrame = true; } return OVRInput.Get(axis, GetOculusController()); } /// /// Returns the Oculus controller script of the controller currently attached to this object. /// public OVRInput.Controller GetOculusController() { if (deviceToTrack == Devices.LeftController) return OVRInput.Controller.LTouch; else if (deviceToTrack == Devices.RightController) return OVRInput.Controller.RTouch; else return OVRInput.Controller.None; } #endif //#if ZED_STEAM_VR #if ZED_SVR_2_0_INPUT /// /// Checks the button state of a given SteamVR boolean action. /// /// Whether to check if the button/action is just pressed, just released, or is being held down. protected bool CheckSteamVRBoolActionState(SteamVR_Action_Boolean action, ControllerButtonState buttonstate) { switch (buttonstate) { case ControllerButtonState.Down: return action.GetLastStateDown(GetSteamVRInputSource()); case ControllerButtonState.Held: return action.GetLastState(GetSteamVRInputSource()); case ControllerButtonState.Up: return action.GetLastStateUp(GetSteamVRInputSource()); default: return false; } } /// /// Returns the axis of a given SteamVR 2D action. /// protected Vector2 CheckSteamVR2DAxis(SteamVR_Action_Vector2 action) { return action.GetAxis(GetSteamVRInputSource()); } public SteamVR_Input_Sources GetSteamVRInputSource() { if (deviceToTrack == Devices.LeftController) return SteamVR_Input_Sources.LeftHand; else if (deviceToTrack == Devices.RightController) return SteamVR_Input_Sources.RightHand; else return SteamVR_Input_Sources.Any; } #elif ZED_STEAM_VR public bool CheckSteamVRButtonState_Legacy(EVRButtonId button, ControllerButtonState state) { switch(state) { case ControllerButtonState.Down: return GetVRButtonDown_Legacy(button); case ControllerButtonState.Held: default: return GetVRButtonHeld_Legacy(button); case ControllerButtonState.Up: return GetVRButtonReleased_Legacy(button); } } /// /// Returns if the VR controller button with the given ID was pressed for the first time this frame. /// /// EVR ID of the button as listed in OpenVR. public bool GetVRButtonDown_Legacy(EVRButtonId buttonid) { if (openvrsystem == null) return false; //If VR isn't running, we can't check. bool washeldlastupdate = (lastcontrollerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L; if (washeldlastupdate == true) return false; //If the key was held last check, it can't be pressed for the first time now. bool isheld = (controllerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L; return isheld; //If we got here, we know it was not down last frame. } /// /// Returns if the VR controller button with the given ID is currently held. /// /// EVR ID of the button as listed in OpenVR. public bool GetVRButtonHeld_Legacy(EVRButtonId buttonid) { if (openvrsystem == null) return false; //If VR isn't running, we can't check. bool isheld = (controllerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L; return isheld; } /// /// Returns if the VR controller button with the given ID was held last frame, but released this frame. /// /// EVR ID of the button as listed in OpenVR. public bool GetVRButtonReleased_Legacy(EVRButtonId buttonid) { if (openvrsystem == null) return false; //If VR isn't running, we can't check. bool washeldlastupdate = (lastcontrollerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L; if (washeldlastupdate == false) return false; //If the key was held last check, it can't be released now. bool isheld = (controllerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L; return !isheld; //If we got here, we know it was not up last frame. } /// /// Returns the value of an axis with the provided ID. /// Note that for single-value axes, the relevant value will be the X in the returned Vector2 (the Y is unused). /// /// public Vector2 CheckSteamVRAxis_Legacy(EVRButtonId buttonid) { //Convert the EVRButtonID enum to the axis number and check if it's not an axis. uint axis = (uint)buttonid - (uint)EVRButtonId.k_EButton_Axis0; if (axis < 0 || axis > 4) { Debug.LogError("Called GetAxis with " + buttonid + ", which is not an axis."); return Vector2.zero; } switch (axis) { case 0: return new Vector2(controllerstate.rAxis0.x, controllerstate.rAxis0.y); case 1: return new Vector2(controllerstate.rAxis1.x, controllerstate.rAxis1.y); case 2: return new Vector2(controllerstate.rAxis2.x, controllerstate.rAxis2.y); case 3: return new Vector2(controllerstate.rAxis3.x, controllerstate.rAxis3.y); case 4: return new Vector2(controllerstate.rAxis4.x, controllerstate.rAxis4.y); default: return Vector2.zero; } } #endif } /// /// List of possible button states, used to check inputs. /// public enum ControllerButtonState { /// /// The button was pressed this frame. /// Down, /// /// The button is being held down - it doesn't matter which frame it started being held. /// Held, /// /// The button was released this frame. /// Up }