/// <summary> /// A coroutine used to update continuous haptics as they're playing /// </summary> /// <returns></returns> protected virtual IEnumerator ContinuousHapticsCoroutine() { _continuousStartedAt = (Timescale == Timescales.ScaledTime) ? Time.time : Time.unscaledTime; _continuousPlaying = true; float elapsedTime = ComputeElapsedTime(); MMVibrationManager.ContinuousHaptic(InitialContinuousIntensity, InitialContinuousSharpness, ContinuousDuration, HapticTypes.Success, this); while (_continuousPlaying && (elapsedTime < ContinuousDuration)) { elapsedTime = ComputeElapsedTime(); float remappedTime = Remap(elapsedTime, 0f, ContinuousDuration, 0f, 1f); float intensity = ContinuousIntensityCurve.Evaluate(remappedTime); float sharpness = ContinuousSharpnessCurve.Evaluate(remappedTime); MMVibrationManager.UpdateContinuousHaptic(intensity, sharpness, true); if (AllowRumble) { #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE MMNVRumble.RumbleContinuous(intensity, sharpness); #endif } yield return(null); } if (_continuousPlaying) { _continuousPlaying = false; MMVibrationManager.StopContinuousHaptic(AllowRumble); } }
/// <summary> /// Stops all running pattern or continuous haptics /// </summary> public static void StopContinuousHaptic(bool alsoRumble = false) { DebugLog("[MMVibrationManager] Stop Continuous Haptic"); MMNViOSCoreHaptics.StopHapticPatterns(); MMNVAndroid.AndroidCancelVibrations(); #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE if (alsoRumble) { MMNVRumble.StopRumble(); } #endif }
/// <summary> /// Plays a transient haptic, signature with more fine control /// </summary> /// <param name="vibrateiOS"></param> /// <param name="iOSIntensity"></param> /// <param name="iOSSharpness"></param> /// <param name="vibrateAndroid"></param> /// <param name="androidIntensity"></param> /// <param name="androidSharpness"></param> /// <param name="vibrateAndroidIfNoSupport"></param> /// <param name="rumble"></param> /// <param name="rumbleLowFrequency"></param> /// <param name="rumbleHighFrequency"></param> /// <param name="controllerID"></param> /// <param name="coroutineSupport"></param> public static void TransientHaptic(bool vibrateiOS, float iOSIntensity, float iOSSharpness, bool vibrateAndroid, float androidIntensity = 1f, float androidSharpness = 1f, bool vibrateAndroidIfNoSupport = false, bool rumble = true, float rumbleLowFrequency = 1f, float rumbleHighFrequency = 1f, int controllerID = -1, MonoBehaviour coroutineSupport = null, bool threaded = true) { if (!_vibrationsActive) { return; } DebugLog("[MMVibrationManager] Transient Haptic"); if (Android() && vibrateAndroid) { if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport) { return; } androidIntensity = Remap(androidIntensity, 0f, 1f, 0, 255); MMNVAndroid.AndroidVibrate(100, (int)(androidIntensity)); } else if (iOS() && vibrateiOS) { if ((iOSVersion >= 13) && HapticsSupported()) { MMNViOSCoreHaptics.PlayTransientHapticPattern(iOSIntensity, iOSSharpness, threaded); _hapticsPlayedOnce = true; } else { if (iOSIntensity < 0.3f) { MMNViOS.iOSTriggerHaptics(HapticTypes.LightImpact); } else if ((iOSIntensity >= 0.3f) && (iOSIntensity < 0.6f)) { MMNViOS.iOSTriggerHaptics(HapticTypes.MediumImpact); } else { MMNViOS.iOSTriggerHaptics(HapticTypes.HeavyImpact); } } } if (rumble && (coroutineSupport != null)) { #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE MMNVRumble.Rumble(rumbleLowFrequency, rumbleHighFrequency, 0.08f, coroutineSupport, controllerID); #endif } }
/// <summary> /// Stops all currently running haptics /// </summary> /// <param name="alsoRumble"></param> public static void StopAllHaptics(bool alsoRumble = false) { if (!_hapticsPlayedOnce) { return; } DebugLog("[MMVibrationManager] Stop all haptics"); MMNViOSCoreHaptics.StopEngine(); MMNVAndroid.AndroidCancelVibrations(); #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE if (alsoRumble) { MMNVRumble.StopRumble(); } #endif }
/// <summary> /// Updates a continuous haptic while it's playing. Not supported on Android for now, until Google adds API support for that /// More options signature /// </summary> /// <param name="ios"></param> /// <param name="iosIntensity"></param> /// <param name="iosSharpness"></param> /// <param name="android"></param> /// <param name="androidIntensity"></param> /// <param name="androidSharpness"></param> /// <param name="rumble"></param> /// <param name="rumbleIntensity"></param> /// <param name="rumbleSharpness"></param> /// <param name="controllerID"></param> public static void UpdateContinuousHaptic(bool ios, float iosIntensity, float iosSharpness, bool android, float androidIntensity, float androidSharpness, bool rumble, float rumbleIntensity, float rumbleSharpness, int controllerID = -1) { if (iOS() && ios) { if ((iOSVersion >= 13) && HapticsSupported()) { MMNViOSCoreHaptics.UpdateContinuousHapticPatternRational(iosIntensity, iosSharpness); _hapticsPlayedOnce = true; } } if (rumble) { #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE MMNVRumble.RumbleContinuous(rumbleIntensity, rumbleSharpness, controllerID); #endif } }
/// <summary> /// Plays a advanced haptic pattern, /// </summary> /// <param name="ios"></param> /// <param name="iOSJSONString"></param> /// <param name="android"></param> /// <param name="androidPattern"></param> /// <param name="androidAmplitudes"></param> /// <param name="androidRepeat"></param> /// <param name="vibrateAndroidIfNoSupport"></param> /// <param name="rumble"></param> /// <param name="rumblePattern"></param> /// <param name="rumbleLowFreqAmplitudes"></param> /// <param name="rumbleHighFreqAmplitudes"></param> /// <param name="rumbleRepeat"></param> /// <param name="fallbackOldiOS"></param> /// <param name="coroutineSupport"></param> /// <param name="controllerID"></param> public static void AdvancedHapticPattern(bool ios, string iOSJSONString, bool android, long[] androidPattern, int[] androidAmplitudes, int androidRepeat, bool vibrateAndroidIfNoSupport, bool rumble, long[] rumblePattern, int[] rumbleLowFreqAmplitudes, int[] rumbleHighFreqAmplitudes, int rumbleRepeat, HapticTypes fallbackOldiOS = HapticTypes.None, MonoBehaviour coroutineSupport = null, int controllerID = -1, bool threaded = false) { if (!_vibrationsActive) { return; } DebugLog("[MMVibrationManager] Advanced Haptic Pattern"); if (Android()) { if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport) { return; } MMNVAndroid.AndroidVibrate(androidPattern, androidAmplitudes, androidRepeat, threaded); } else if (iOS()) { if ((iOSVersion >= 13) && HapticsSupported()) { MMNViOSCoreHaptics.PlayCoreHapticsFromJSON(iOSJSONString, threaded); _hapticsPlayedOnce = true; } else { MMNViOS.iOSTriggerHaptics(fallbackOldiOS); } } #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE if (coroutineSupport != null) { MMNVRumble.Rumble(rumblePattern, rumbleLowFreqAmplitudes, rumbleHighFreqAmplitudes, rumbleRepeat, coroutineSupport, controllerID); } #endif }
/// <summary> /// Plays a continuous haptic, full options signature /// </summary> /// <param name="vibrateiOS"></param> /// <param name="iOSIntensity"></param> /// <param name="iOSSharpness"></param> /// <param name="fallbackOldiOS"></param> /// <param name="vibrateAndroid"></param> /// <param name="androidIntensity"></param> /// <param name="androidSharpness"></param> /// <param name="vibrateAndroidIfNoSupport"></param> /// <param name="rumble"></param> /// <param name="rumbleLowFrequency"></param> /// <param name="rumbleHighFrequency"></param> /// <param name="controllerID"></param> /// <param name="duration">the duration in seconds</param> /// <param name="mono">a monobehaviour to use to sustain this haptic</param> /// <param name="threaded">whether to call this on the main thread (false) or a secondary one (true)</param> /// <param name="fullIntensity">whether to allow for full intensity control for subsequent updates</param> public static void ContinuousHaptic(bool vibrateiOS, float iOSIntensity, float iOSSharpness, HapticTypes fallbackOldiOS, bool vibrateAndroid, float androidIntensity, float androidSharpness, bool vibrateAndroidIfNoSupport, bool rumble, float rumbleLowFrequency, float rumbleHighFrequency, int controllerID, float duration, MonoBehaviour mono = null, bool threaded = false, bool fullIntensity = true) { if (!_vibrationsActive) { return; } DebugLog("[MMVibrationManager] Continuous Haptic"); if (Android() && vibrateAndroid) { if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport) { return; } androidIntensity = Remap(androidIntensity, 0f, 1f, 0, 255); MMNVAndroid.AndroidVibrate((long)(duration * 1000), (int)(androidIntensity)); } else if (iOS() && vibrateiOS) { if ((iOSVersion >= 13) && HapticsSupported()) { MMNViOSCoreHaptics.PlayContinuousHapticPattern(iOSIntensity, iOSSharpness, duration, mono, threaded, fullIntensity); _hapticsPlayedOnce = true; } else { MMNViOS.iOSTriggerHaptics(fallbackOldiOS); } } if (rumble && (mono != null)) { #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE MMNVRumble.RumbleContinuous(rumbleLowFrequency, rumbleHighFrequency, controllerID); #endif } }
/// <summary> /// Triggers a haptic feedback of the specified type /// </summary> /// <param name="type">Type.</param> public static void Haptic(HapticTypes type, bool defaultToRegularVibrate = false, bool alsoRumble = false, MonoBehaviour coroutineSupport = null, int controllerID = -1) { if (!_vibrationsActive) { return; } DebugLog("[MMVibrationManager] Regular Haptic"); if (Android()) { switch (type) { case HapticTypes.None: // do nothing break; case HapticTypes.Selection: MMNVAndroid.AndroidVibrate(LightDuration, LightAmplitude); break; case HapticTypes.Success: MMNVAndroid.AndroidVibrate(_successPattern, _successPatternAmplitude, -1); break; case HapticTypes.Warning: MMNVAndroid.AndroidVibrate(_warningPattern, _warningPatternAmplitude, -1); break; case HapticTypes.Failure: MMNVAndroid.AndroidVibrate(_failurePattern, _failurePatternAmplitude, -1); break; case HapticTypes.LightImpact: MMNVAndroid.AndroidVibrate(_lightImpactPattern, _lightImpactPatternAmplitude, -1); break; case HapticTypes.MediumImpact: MMNVAndroid.AndroidVibrate(_mediumImpactPattern, _mediumImpactPatternAmplitude, -1); break; case HapticTypes.HeavyImpact: MMNVAndroid.AndroidVibrate(_HeavyImpactPattern, _HeavyImpactPatternAmplitude, -1); break; case HapticTypes.RigidImpact: MMNVAndroid.AndroidVibrate(_rigidImpactPattern, _rigidImpactPatternAmplitude, -1); break; case HapticTypes.SoftImpact: MMNVAndroid.AndroidVibrate(_softImpactPattern, _softImpactPatternAmplitude, -1); break; } } else if (iOS()) { MMNViOS.iOSTriggerHaptics(type, defaultToRegularVibrate); } if (alsoRumble && (coroutineSupport != null)) { #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE switch (type) { case HapticTypes.None: // do nothing break; case HapticTypes.Selection: MMNVRumble.Rumble(_rumbleLight.x, _rumbleMedium.y, _rumbleLight.z, coroutineSupport, controllerID); break; case HapticTypes.Success: MMNVRumble.Rumble(_successPattern, _successPatternAmplitude, -1, coroutineSupport, controllerID); break; case HapticTypes.Warning: MMNVRumble.Rumble(_warningPattern, _warningPatternAmplitude, -1, coroutineSupport, controllerID); break; case HapticTypes.Failure: MMNVRumble.Rumble(_failurePattern, _failurePatternAmplitude, -1, coroutineSupport, controllerID); break; case HapticTypes.LightImpact: MMNVRumble.Rumble(_rumbleLight.x, _rumbleLight.y, _rumbleLight.z, coroutineSupport, controllerID); break; case HapticTypes.MediumImpact: MMNVRumble.Rumble(_rumbleMedium.x, _rumbleMedium.y, _rumbleMedium.z, coroutineSupport, controllerID); break; case HapticTypes.HeavyImpact: MMNVRumble.Rumble(_rumbleHeavy.x, _rumbleHeavy.y, _rumbleHeavy.z, coroutineSupport, controllerID); break; case HapticTypes.RigidImpact: MMNVRumble.Rumble(_rumbleRigid.x, _rumbleRigid.y, _rumbleRigid.z, coroutineSupport, controllerID); break; case HapticTypes.SoftImpact: MMNVRumble.Rumble(_rumbleSoft.x, _rumbleSoft.y, _rumbleSoft.z, coroutineSupport, controllerID); break; } #endif } }