|
|
- // Controller Haptics|Interactions|30030
- namespace VRTK
- {
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
-
- /// <summary>
- /// A collection of static methods for calling haptic functions on a given controller.
- /// </summary>
- /// <remarks>
- /// **Script Usage:**
- /// > There is no requirement to add this script to a GameObject as all of the public methods are static and can be called directly e.g. `VRTK_ControllerHaptics.TriggerHapticPulse(ref, 1f)`.
- /// </remarks>
- public class VRTK_ControllerHaptics : MonoBehaviour
- {
- protected static VRTK_ControllerHaptics instance;
- protected Dictionary<VRTK_ControllerReference, Coroutine> hapticLoopCoroutines = new Dictionary<VRTK_ControllerReference, Coroutine>();
-
- /// <summary>
- /// The TriggerHapticPulse/2 method calls a single haptic pulse call on the controller for a single tick.
- /// </summary>
- /// <param name="controllerReference">The reference to the controller to activate the haptic feedback on.</param>
- /// <param name="strength">The intensity of the rumble of the controller motor. `0` to `1`.</param>
- public static void TriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength)
- {
- SetupInstance();
- if (instance != null)
- {
- instance.InternalTriggerHapticPulse(controllerReference, strength);
- }
- }
-
- /// <summary>
- /// The TriggerHapticPulse/4 method calls a haptic pulse for a specified amount of time rather than just a single tick. Each pulse can be separated by providing a `pulseInterval` to pause between each haptic pulse.
- /// </summary>
- /// <param name="controllerReference">The reference to the controller to activate the haptic feedback on.</param>
- /// <param name="strength">The intensity of the rumble of the controller motor. `0` to `1`.</param>
- /// <param name="duration">The length of time the rumble should continue for.</param>
- /// <param name="pulseInterval">The interval to wait between each haptic pulse.</param>
- public static void TriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength, float duration, float pulseInterval)
- {
- SetupInstance();
- if (instance != null)
- {
- instance.InternalTriggerHapticPulse(controllerReference, strength, duration, pulseInterval);
- }
- }
-
- /// <summary>
- /// The TriggerHapticPulse/2 method calls a haptic pulse based on a given audio clip.
- /// </summary>
- /// <param name="controllerReference">The reference to the controller to activate the haptic feedback on.</param>
- /// <param name="clip">The audio clip to use for the haptic pattern.</param>
- public static void TriggerHapticPulse(VRTK_ControllerReference controllerReference, AudioClip clip)
- {
- SetupInstance();
- if (instance != null)
- {
- instance.InternalTriggerHapticPulse(controllerReference, clip);
- }
- }
-
- /// <summary>
- /// The CancelHapticPulse method cancels the existing running haptic pulse on the given controller index.
- /// </summary>
- /// <param name="controllerReference">The reference to the controller to cancel the haptic feedback on.</param>
- public static void CancelHapticPulse(VRTK_ControllerReference controllerReference)
- {
- SetupInstance();
- if (instance != null)
- {
- instance.InternalCancelHapticPulse(controllerReference);
- }
- }
-
- protected virtual void OnDisable()
- {
- StopAllCoroutines();
- hapticLoopCoroutines.Clear();
- }
-
- protected static void SetupInstance()
- {
- if (instance == null && VRTK_SDKManager.ValidInstance())
- {
- instance = VRTK_SDKManager.instance.gameObject.AddComponent<VRTK_ControllerHaptics>();
- }
- }
-
- protected virtual void InternalTriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength)
- {
- InternalCancelHapticPulse(controllerReference);
- float hapticPulseStrength = Mathf.Clamp(strength, 0f, 1f);
- VRTK_SDK_Bridge.HapticPulse(controllerReference, hapticPulseStrength);
- }
-
- protected virtual void InternalTriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength, float duration, float pulseInterval)
- {
- InternalCancelHapticPulse(controllerReference);
- float hapticPulseStrength = Mathf.Clamp(strength, 0f, 1f);
- SDK_ControllerHapticModifiers hapticModifiers = VRTK_SDK_Bridge.GetHapticModifiers();
- Coroutine hapticLoop = StartCoroutine(SimpleHapticPulseRoutine(controllerReference, duration * hapticModifiers.durationModifier, hapticPulseStrength, pulseInterval * hapticModifiers.intervalModifier));
- VRTK_SharedMethods.AddDictionaryValue(hapticLoopCoroutines, controllerReference, hapticLoop);
- }
-
- protected virtual void InternalTriggerHapticPulse(VRTK_ControllerReference controllerReference, AudioClip clip)
- {
- InternalCancelHapticPulse(controllerReference);
- if (!VRTK_SDK_Bridge.HapticPulse(controllerReference, clip))
- {
- //If the SDK Bridge doesn't support audio clips then defer to a local version
- Coroutine hapticLoop = StartCoroutine(AudioClipHapticsRoutine(controllerReference, clip));
- VRTK_SharedMethods.AddDictionaryValue(hapticLoopCoroutines, controllerReference, hapticLoop);
- }
- }
-
- protected virtual void InternalCancelHapticPulse(VRTK_ControllerReference controllerReference)
- {
- Coroutine currentHapticLoopRoutine = VRTK_SharedMethods.GetDictionaryValue(hapticLoopCoroutines, controllerReference);
- if (currentHapticLoopRoutine != null)
- {
- StopCoroutine(currentHapticLoopRoutine);
- hapticLoopCoroutines.Remove(controllerReference);
- }
- }
-
- protected virtual IEnumerator SimpleHapticPulseRoutine(VRTK_ControllerReference controllerReference, float duration, float hapticPulseStrength, float pulseInterval)
- {
- if (pulseInterval <= 0)
- {
- yield break;
- }
-
- while (duration > 0)
- {
- VRTK_SDK_Bridge.HapticPulse(controllerReference, hapticPulseStrength);
- yield return new WaitForSeconds(pulseInterval);
- duration -= pulseInterval;
- }
- }
-
- protected virtual IEnumerator AudioClipHapticsRoutine(VRTK_ControllerReference controllerReference, AudioClip clip)
- {
- SDK_ControllerHapticModifiers hapticModifiers = VRTK_SDK_Bridge.GetHapticModifiers();
- float hapticScalar = hapticModifiers.maxHapticVibration;
- float[] audioData = new float[hapticModifiers.hapticsBufferSize];
- int sampleOffset = -hapticModifiers.hapticsBufferSize;
- float startTime = Time.time;
- float length = clip.length / 1;
- float endTime = startTime + length;
- float sampleRate = clip.samples;
- while (Time.time <= endTime)
- {
- float lerpVal = (Time.time - startTime) / length;
- int sampleIndex = (int)(sampleRate * lerpVal);
- if (sampleIndex >= sampleOffset + hapticModifiers.hapticsBufferSize)
- {
- clip.GetData(audioData, sampleIndex);
- sampleOffset = sampleIndex;
- }
- float currentSample = Mathf.Abs(audioData[sampleIndex - sampleOffset]);
- ushort hapticStrength = (ushort)(hapticScalar * currentSample);
- VRTK_SDK_Bridge.HapticPulse(controllerReference, hapticStrength);
- yield return null;
- }
- }
- }
- }
|