// Interact Haptics|Interactables|35020 namespace VRTK { using UnityEngine; /// /// Event Payload /// /// The reference to the controller to perform haptics on. public struct InteractHapticsEventArgs { public VRTK_ControllerReference controllerReference; } /// /// Event Payload /// /// this object /// public delegate void InteractHapticsEventHandler(object sender, InteractHapticsEventArgs e); /// /// Provides controller haptics upon interaction with the specified Interactable Object. /// /// /// **Required Components:** /// * `VRTK_InteractableObject` - The Interactable Object component to detect interactions on. This must be applied on the same GameObject as this script if one is not provided via the `Object To Affect` parameter. /// /// **Script Usage:** /// * Place the `VRTK_InteractHaptics` script on either: /// * The GameObject of the Interactable Object to detect interactions on. /// * Any other scene GameObject and provide a valid `VRTK_InteractableObject` component to the `Object To Affect` parameter of this script. /// [AddComponentMenu("VRTK/Scripts/Interactions/Interactables/VRTK_InteractHaptics")] public class VRTK_InteractHaptics : VRTK_InteractableListener { [Header("Haptics On Near Touch Settings")] [Tooltip("Denotes the audio clip to use to rumble the controller on near touch.")] public AudioClip clipOnNearTouch; [Tooltip("Denotes how strong the rumble in the controller will be on near touch.")] [Range(0, 1)] public float strengthOnNearTouch = 0; [Tooltip("Denotes how long the rumble in the controller will last on near touch.")] public float durationOnNearTouch = 0f; [Tooltip("Denotes interval betweens rumble in the controller on near touch.")] public float intervalOnNearTouch = minInterval; [Tooltip("If this is checked then the rumble will be cancelled when the controller is no longer near touching.")] public bool cancelOnNearUntouch = true; [Header("Haptics On Touch Settings")] [Tooltip("Denotes the audio clip to use to rumble the controller on touch.")] public AudioClip clipOnTouch; [Tooltip("Denotes how strong the rumble in the controller will be on touch.")] [Range(0, 1)] public float strengthOnTouch = 0; [Tooltip("Denotes how long the rumble in the controller will last on touch.")] public float durationOnTouch = 0f; [Tooltip("Denotes interval betweens rumble in the controller on touch.")] public float intervalOnTouch = minInterval; [Tooltip("If this is checked then the rumble will be cancelled when the controller is no longer touching.")] public bool cancelOnUntouch = true; [Header("Haptics On Grab Settings")] [Tooltip("Denotes the audio clip to use to rumble the controller on grab.")] public AudioClip clipOnGrab; [Tooltip("Denotes how strong the rumble in the controller will be on grab.")] [Range(0, 1)] public float strengthOnGrab = 0; [Tooltip("Denotes how long the rumble in the controller will last on grab.")] public float durationOnGrab = 0f; [Tooltip("Denotes interval betweens rumble in the controller on grab.")] public float intervalOnGrab = minInterval; [Tooltip("If this is checked then the rumble will be cancelled when the controller is no longer grabbing.")] public bool cancelOnUngrab = true; [Header("Haptics On Use Settings")] [Tooltip("Denotes the audio clip to use to rumble the controller on use.")] public AudioClip clipOnUse; [Tooltip("Denotes how strong the rumble in the controller will be on use.")] [Range(0, 1)] public float strengthOnUse = 0; [Tooltip("Denotes how long the rumble in the controller will last on use.")] public float durationOnUse = 0f; [Tooltip("Denotes interval betweens rumble in the controller on use.")] public float intervalOnUse = minInterval; [Tooltip("If this is checked then the rumble will be cancelled when the controller is no longer using.")] public bool cancelOnUnuse = true; [Header("Custom Settings")] [Tooltip("The Interactable Object to initiate the haptics from. If this is left blank, then the Interactable Object will need to be on the current or a parent GameObject.")] public VRTK_InteractableObject objectToAffect; /// /// Emitted when the haptics are from a near touch. /// public event InteractHapticsEventHandler InteractHapticsNearTouched; /// /// Emitted when the haptics are from a touch. /// public event InteractHapticsEventHandler InteractHapticsTouched; /// /// Emitted when the haptics are from a grab. /// public event InteractHapticsEventHandler InteractHapticsGrabbed; /// /// Emitted when the haptics are from a use. /// public event InteractHapticsEventHandler InteractHapticsUsed; protected const float minInterval = 0.05f; public virtual void OnInteractHapticsNearTouched(InteractHapticsEventArgs e) { if (InteractHapticsNearTouched != null) { InteractHapticsNearTouched(this, e); } } public virtual void OnInteractHapticsTouched(InteractHapticsEventArgs e) { if (InteractHapticsTouched != null) { InteractHapticsTouched(this, e); } } public virtual void OnInteractHapticsGrabbed(InteractHapticsEventArgs e) { if (InteractHapticsGrabbed != null) { InteractHapticsGrabbed(this, e); } } public virtual void OnInteractHapticsUsed(InteractHapticsEventArgs e) { if (InteractHapticsUsed != null) { InteractHapticsUsed(this, e); } } /// /// The CancelHaptics method cancels any existing haptic feedback on the given controller. /// /// public virtual void CancelHaptics(VRTK_ControllerReference controllerReference) { VRTK_ControllerHaptics.CancelHapticPulse(controllerReference); } /// /// The HapticsOnNearTouch method triggers the haptic feedback on the given controller for the settings associated with near touch. /// /// The reference to the controller to activate the haptic feedback on. public virtual void HapticsOnNearTouch(VRTK_ControllerReference controllerReference) { if (clipOnNearTouch != null) { VRTK_ControllerHaptics.TriggerHapticPulse(controllerReference, clipOnNearTouch); } else if (strengthOnNearTouch > 0 && durationOnNearTouch > 0f) { TriggerHapticPulse(controllerReference, strengthOnNearTouch, durationOnNearTouch, intervalOnNearTouch); } else { VRTK_ControllerHaptics.CancelHapticPulse(controllerReference); } OnInteractHapticsNearTouched(SetEventPayload(controllerReference)); } /// /// The HapticsOnTouch method triggers the haptic feedback on the given controller for the settings associated with touch. /// /// The reference to the controller to activate the haptic feedback on. public virtual void HapticsOnTouch(VRTK_ControllerReference controllerReference) { if (clipOnTouch != null) { VRTK_ControllerHaptics.TriggerHapticPulse(controllerReference, clipOnTouch); } else if (strengthOnTouch > 0 && durationOnTouch > 0f) { TriggerHapticPulse(controllerReference, strengthOnTouch, durationOnTouch, intervalOnTouch); } else { VRTK_ControllerHaptics.CancelHapticPulse(controllerReference); } OnInteractHapticsTouched(SetEventPayload(controllerReference)); } /// /// The HapticsOnGrab method triggers the haptic feedback on the given controller for the settings associated with grab. /// /// The reference to the controller to activate the haptic feedback on. public virtual void HapticsOnGrab(VRTK_ControllerReference controllerReference) { if (clipOnGrab != null) { VRTK_ControllerHaptics.TriggerHapticPulse(controllerReference, clipOnGrab); } else if (strengthOnGrab > 0 && durationOnGrab > 0f) { TriggerHapticPulse(controllerReference, strengthOnGrab, durationOnGrab, intervalOnGrab); } else { VRTK_ControllerHaptics.CancelHapticPulse(controllerReference); } OnInteractHapticsGrabbed(SetEventPayload(controllerReference)); } /// /// The HapticsOnUse method triggers the haptic feedback on the given controller for the settings associated with use. /// /// The reference to the controller to activate the haptic feedback on. public virtual void HapticsOnUse(VRTK_ControllerReference controllerReference) { if (clipOnUse != null) { VRTK_ControllerHaptics.TriggerHapticPulse(controllerReference, clipOnUse); } else if (strengthOnUse > 0 && durationOnUse > 0f) { TriggerHapticPulse(controllerReference, strengthOnUse, durationOnUse, intervalOnUse); } else { VRTK_ControllerHaptics.CancelHapticPulse(controllerReference); } OnInteractHapticsUsed(SetEventPayload(controllerReference)); } protected virtual void OnEnable() { EnableListeners(); } protected virtual void OnDisable() { DisableListeners(); } protected override bool SetupListeners(bool throwError) { objectToAffect = (objectToAffect != null ? objectToAffect : GetComponentInParent()); if (objectToAffect != null) { objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.NearUntouch, CancelNearTouchHaptics); objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.Untouch, CancelTouchHaptics); objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.Ungrab, CancelGrabHaptics); objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.Unuse, CancelUseHaptics); objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.NearTouch, NearTouchHaptics); objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.Touch, TouchHaptics); objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.Grab, GrabHaptics); objectToAffect.SubscribeToInteractionEvent(VRTK_InteractableObject.InteractionType.Use, UseHaptics); return true; } else if (throwError) { VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_InteractHaptics", "VRTK_InteractableObject", "the same or parent")); } return false; } protected override void TearDownListeners() { if (objectToAffect != null) { objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.NearUntouch, CancelNearTouchHaptics); objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.Untouch, CancelTouchHaptics); objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.Ungrab, CancelGrabHaptics); objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.Unuse, CancelUseHaptics); objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.NearTouch, NearTouchHaptics); objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.Touch, TouchHaptics); objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.Grab, GrabHaptics); objectToAffect.UnsubscribeFromInteractionEvent(VRTK_InteractableObject.InteractionType.Use, UseHaptics); } } protected virtual void TriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength, float duration, float interval) { VRTK_ControllerHaptics.TriggerHapticPulse(controllerReference, strength, duration, (interval >= minInterval ? interval : minInterval)); } protected virtual InteractHapticsEventArgs SetEventPayload(VRTK_ControllerReference givenControllerReference) { InteractHapticsEventArgs e; e.controllerReference = givenControllerReference; return e; } protected virtual void NearTouchHaptics(object sender, InteractableObjectEventArgs e) { VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(e.interactingObject); if (VRTK_ControllerReference.IsValid(controllerReference)) { HapticsOnNearTouch(controllerReference); } } protected virtual void TouchHaptics(object sender, InteractableObjectEventArgs e) { VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(e.interactingObject); if (VRTK_ControllerReference.IsValid(controllerReference)) { HapticsOnTouch(controllerReference); } } protected virtual void GrabHaptics(object sender, InteractableObjectEventArgs e) { VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(e.interactingObject); if (VRTK_ControllerReference.IsValid(controllerReference)) { HapticsOnGrab(controllerReference); } } protected virtual void UseHaptics(object sender, InteractableObjectEventArgs e) { VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(e.interactingObject); if (VRTK_ControllerReference.IsValid(controllerReference)) { HapticsOnUse(controllerReference); } } protected virtual void CancelOn(GameObject givenObject) { VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(givenObject); if (VRTK_ControllerReference.IsValid(controllerReference)) { CancelHaptics(controllerReference); } } protected virtual void CancelNearTouchHaptics(object sender, InteractableObjectEventArgs e) { if (cancelOnNearUntouch) { CancelOn(e.interactingObject); } } protected virtual void CancelTouchHaptics(object sender, InteractableObjectEventArgs e) { if (cancelOnUntouch) { CancelOn(e.interactingObject); } } protected virtual void CancelGrabHaptics(object sender, InteractableObjectEventArgs e) { if (cancelOnUngrab) { CancelOn(e.interactingObject); } } protected virtual void CancelUseHaptics(object sender, InteractableObjectEventArgs e) { if (cancelOnUnuse) { CancelOn(e.interactingObject); } } } }