// AHAP ------------------------------------------------------------------------------------------------ public virtual void PlayAHAP(int index) { Logo.Shaking = true; // for the purpose of the demo, and to be able to observe the difference, if any, on certain devices, // the first 4 effects (dice, drums, game over, heart beats) will be called on the main thread, and the remaining ones on a secondary thread if (index < 5) { MMVibrationManager.AdvancedHapticPattern(DemoItems[index].AHAPFile.text, DemoItems[index].WaveFormAsset.WaveForm.Pattern, DemoItems[index].WaveFormAsset.WaveForm.Amplitudes, -1, DemoItems[index].RumbleWaveFormAsset.WaveForm.Pattern, DemoItems[index].RumbleWaveFormAsset.WaveForm.LowFrequencyAmplitudes, DemoItems[index].RumbleWaveFormAsset.WaveForm.HighFrequencyAmplitudes, -1, HapticTypes.LightImpact, this, -1, false); DemoItems[index].AssociatedSound.Play(); StartCoroutine(ChangeIcon(DemoItems[index].AssociatedSprite)); } else { MMVibrationManager.AdvancedHapticPattern(DemoItems[index].AHAPFile.text, DemoItems[index].WaveFormAsset.WaveForm.Pattern, DemoItems[index].WaveFormAsset.WaveForm.Amplitudes, -1, DemoItems[index].RumbleWaveFormAsset.WaveForm.Pattern, DemoItems[index].RumbleWaveFormAsset.WaveForm.LowFrequencyAmplitudes, DemoItems[index].RumbleWaveFormAsset.WaveForm.HighFrequencyAmplitudes, -1, HapticTypes.LightImpact, this, -1, true); DemoItems[index].AssociatedSound.Play(); StartCoroutine(ChangeIcon(DemoItems[index].AssociatedSprite)); } }
protected virtual void UpdateUI() { if (Knob.Active) { // start dent if (Time.time - _lastStartClickAt < StartClickDuration) { float elapsedTime = StartClickCurve.Evaluate((Time.time - _lastStartClickAt) * (1 / StartClickDuration)); Knob._rectTransform.localScale = Vector3.one + Vector3.one * elapsedTime * 0.05f; Knob._image.color = Color.Lerp(ActiveColor, Color.white, elapsedTime); } // other dents foreach (float f in Dents) { if (((_knobValue >= f) && (_knobValueLastFrame < f)) || ((_knobValue <= f) && (_knobValueLastFrame > f))) { _lastDentAt = Time.time; break; } } if (Time.time - _lastDentAt < DentDuration) { float elapsedTime = StartClickCurve.Evaluate((Time.time - _lastDentAt) * (1 / DentDuration)); Knob._rectTransform.localScale = Vector3.one + Vector3.one * elapsedTime * 0.02f; Knob._image.color = Color.Lerp(ActiveColor, Color.white, elapsedTime * 0.05f); if (MMVibrationManager.iOS()) { MMVibrationManager.TransientHaptic(0.4f, 1f); } } } // gas bar PowerBar.UpdateBar(Power, 0f, MaximumPowerDuration); // power bars if (CarSpeed <= 0.1f) { for (int i = 0; i < SpeedBars.Count; i++) { SpeedBars[i].SetActive(false); } } else { int barsAmount = (int)(CarSpeed * 5f); for (int i = 0; i < SpeedBars.Count; i++) { if (i <= barsAmount) { SpeedBars[i].SetActive(true); } else { SpeedBars[i].SetActive(false); } } } }
protected virtual void UpdateContinuousDemo() { if (_timeLeft > 0f) { ContinuousProgressBar.UpdateBar(_timeLeft, 0f, ContinuousDuration); _timeLeft -= Time.deltaTime; Logo.Shaking = true; TargetCurve.Move = true; Logo.Intensity = NiceVibrationsDemoHelpers.Remap(ContinuousIntensity, 0f, 1f, 1f, 8f); Logo.Sharpness = NiceVibrationsDemoHelpers.Remap(ContinuousSharpness, 0f, 1f, 10f, 25f); } else { ContinuousProgressBar.UpdateBar(0f, 0f, ContinuousDuration); Logo.Shaking = false; TargetCurve.Move = false; if (_continuousActive) { MMVibrationManager.StopContinuousHaptic(true); OnHapticsStopped(); } } if ((_sharpnessLastFrame != ContinuousSharpness) || (_intensityLastFrame != ContinuousIntensity)) { TargetCurve.UpdateCurve(ContinuousIntensity, ContinuousSharpness); } _intensityLastFrame = ContinuousIntensity; _sharpnessLastFrame = ContinuousSharpness; }
// AHAP ------------------------------------------------------------------------------------------------ public virtual void PlayAHAP(int index) { Logo.Shaking = true; MMVibrationManager.AdvancedHapticPattern(DemoItems[index].AHAPFile.text, DemoItems[index].WaveFormAsset.WaveForm.Pattern, DemoItems[index].WaveFormAsset.WaveForm.Amplitudes, -1, HapticTypes.LightImpact); DemoItems[index].AssociatedSound.Play(); StartCoroutine(ChangeIcon(DemoItems[index].AssociatedSprite)); }
/// <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> /// Returns true if the device running the game has amplitude control /// </summary> /// <returns></returns> public static bool AndroidHasAmplitudeControl() { if (!MMVibrationManager.Android()) { return(false); } return(AndroidVibrator.Call <bool>("hasAmplitudeControl")); }
/// <summary> /// Returns true if the device running the game has vibrations /// </summary> /// <returns></returns> public static bool AndroidHasVibrator() { if (!MMVibrationManager.Android()) { return(false); } return(AndroidVibrator.Call <bool>("hasVibrator")); }
/// <summary> /// Stops all Android vibrations that may be active /// </summary> public static void AndroidCancelVibrations() { if (!MMVibrationManager.Android()) { return; } AndroidVibrator.Call("cancel"); }
public virtual void TransientHapticsButton() { MMVibrationManager.TransientHaptic(TransientIntensity, TransientSharpness); StartCoroutine(Logo.Shake(0.2f)); DebugAudioTransient.volume = TransientIntensity; DebugAudioTransient.pitch = 0.5f + TransientSharpness / 2f; DebugAudioTransient.Play(); }
/// <summary> /// Releases the feedback generators, usually you'll want to call this at OnDisable(); or anytime you know you won't need /// vibrations anymore. /// </summary> public static void iOSReleaseHaptics() { if (!MMVibrationManager.iOS()) { return; } MMNViOS_ReleaseFeedbackGenerators(); }
protected virtual void UpdateContinuous() { if (_continuousActive) { MMVibrationManager.UpdateContinuousHaptic(ContinuousIntensity, ContinuousSharpness, true, -1, true); DebugAudioContinuous.volume = ContinuousIntensity; DebugAudioContinuous.pitch = 0.5f + ContinuousSharpness / 2f; } }
public virtual void HitPusher() { HitPusherParticles.Play(); MMVibrationManager.TransientHaptic(0.85f, 0.05f, true, this); TransientAudioSource.volume = 0.1f; StartCoroutine(LogoShaker.Shake(0.2f)); TransientAudioSource.Play(); _ballAnimator.SetTrigger(_hitAnimationParameter); }
/// <summary> /// Enables or disables all haptics called via this class /// </summary> /// <param name="status"></param> public static void SetHapticsActive(bool status) { DebugLog("[MMVibrationManager] Set haptics active : " + status); _vibrationsActive = status; if (!status) { MMVibrationManager.StopContinuousHaptic(true); } }
/// <summary> /// Requests a default vibration on Android, for the specified duration, in milliseconds /// </summary> /// <param name="milliseconds">Milliseconds.</param> public static void AndroidVibrate(long milliseconds) { if (!MMVibrationManager.Android()) { return; } AndroidVibrateMethodRawClassParameters[0].j = milliseconds; AndroidJNI.CallVoidMethod(AndroidVibrator.GetRawObject(), AndroidVibrateMethodRawClass, AndroidVibrateMethodRawClassParameters); }
/// <summary> /// Call this method to initialize the haptics. If you forget to do it, Nice Vibrations will do it for you the first time you /// call iOSTriggerHaptics. It's better if you do it though. /// </summary> public static void iOSInitializeHaptics() { if (!MMVibrationManager.iOS()) { return; } MMNViOS_InstantiateFeedbackGenerators(); iOSHapticsInitialized = true; }
protected virtual void HitWall() { float intensity = _rigidBody.velocity.magnitude / 100f; MMVibrationManager.TransientHaptic(intensity, 0.7f, true, this); TransientAudioSource.volume = intensity; StartCoroutine(LogoShaker.Shake(0.2f)); TransientAudioSource.Play(); _ballAnimator.SetTrigger(_hitAnimationParameter); }
protected virtual void Transition(int previous, int next, bool goingRight) { MMVibrationManager.StopAllHaptics(true); if (_transitionCoroutine != null) { StopCoroutine(_transitionCoroutine); } _transitionCoroutine = StartCoroutine(TransitionCoroutine(previous, next, goingRight)); }
protected virtual void Start() { _text.text = Version; if (MMVibrationManager.iOS()) { _text.text += " iOS " + MMVibrationManager.iOSVersion.ToString(); } if (MMVibrationManager.Android()) { _text.text += " Android " + MMNVAndroid.AndroidSDKVersion().ToString(); } }
protected virtual void DisplayInformation() { if (MMVibrationManager.Android()) { _platformString = "API version " + MMVibrationManager.AndroidSDKVersion().ToString(); } else if (MMVibrationManager.iOS()) { _platformString = "iOS " + MMVibrationManager.iOSSDKVersion(); } else { _platformString = Application.platform + ", not supported by Nice Vibrations for now."; } DebugTextBox.text = "Platform : " + _platformString + "\n Nice Vibrations v1.2"; }
// Requests a vibration on Android for the specified pattern and optional repeat // Straight out of the Android documentation : // Pass in an array of ints that are the durations for which to turn on or off the vibrator in milliseconds. // The first value indicates the number of milliseconds to wait before turning the vibrator on. // The next value indicates the number of milliseconds for which to keep the vibrator on before turning it off. // Subsequent values alternate between durations in milliseconds to turn the vibrator off or to turn the vibrator on. // repeat: the index into pattern at which to repeat, or -1 if you don't want to repeat. public static void AndroidVibrate(long[] pattern, int repeat) { if (!MMVibrationManager.Android()) { return; } if ((AndroidSDKVersion() < 26)) { AndroidVibrator.Call("vibrate", pattern, repeat); } else { AndroidVibrationEffectClassInitialization(); VibrationEffect = VibrationEffectClass.CallStatic <AndroidJavaObject>("createWaveform", new object[] { pattern, repeat }); AndroidVibrator.Call("vibrate", VibrationEffect); } }
/// <summary> /// Requests a vibration of the specified amplitude and duration. If amplitude is not supported by the device's SDK, a default vibration will be requested /// </summary> /// <param name="milliseconds">Milliseconds.</param> /// <param name="amplitude">Amplitude.</param> public static void AndroidVibrate(long milliseconds, int amplitude) { if (!MMVibrationManager.Android()) { return; } // amplitude is only supported after API26 if ((AndroidSDKVersion() < 26)) { AndroidVibrate(milliseconds); } else { AndroidVibrationEffectClassInitialization(); VibrationEffect = VibrationEffectClass.CallStatic <AndroidJavaObject>("createOneShot", new object[] { milliseconds, amplitude }); AndroidVibrator.Call("vibrate", VibrationEffect); } }
public virtual void ContinuousHapticsButton() { if (!_continuousActive) { // START MMVibrationManager.ContinuousHaptic(ContinuousIntensity, ContinuousSharpness, ContinuousDuration, HapticTypes.LightImpact, this, true, -1, true); _timeLeft = ContinuousDuration; ContinuousButtonText.text = "Stop continuous haptic pattern"; DurationSlider.interactable = false; _continuousActive = true; DebugAudioContinuous.Play(); } else { // STOP MMVibrationManager.StopContinuousHaptic(true); ResetPlayState(); } }
public virtual void OnPointerExit(PointerEventData data) { _eventPosition = _pointerEventData.position; _newTargetPosition = GetWorldPosition(_eventPosition); _newTargetPosition = Vector2.ClampMagnitude(_newTargetPosition - _neutralPosition, MaxRange); _newTargetPosition = _neutralPosition + _newTargetPosition; _newTargetPosition.z = _initialZPosition; _dragging = false; _dragEndedPosition = _newTargetPosition; _dragEndedAt = Time.time; _dragResetDirection = _dragEndedPosition - _neutralPosition; _pointerOn = false; TargetAnimator.SetTrigger(_sparkAnimationParameter); SpringAudioSource.Play(); MMVibrationManager.AdvancedHapticPattern(AHAPFile.text, _wobbleAndroidPattern, _wobbleAndroidAmplitude, -1, HapticTypes.LightImpact); }
/// <summary> /// When this feedback gets played /// </summary> /// <param name="position"></param> /// <param name="attenuation"></param> protected virtual void TestVibration() { Vector3 position = this.transform.position; switch (HapticMethod) { case HapticMethods.AdvancedPattern: string iOSString = (AHAPFileForIOS == null) ? "" : AHAPFileForIOS.text; long[] androidPattern = (AndroidWaveFormFile == null) ? null : AndroidWaveFormFile.WaveForm.Pattern; int[] androidAmplitude = (AndroidWaveFormFile == null) ? null : AndroidWaveFormFile.WaveForm.Amplitudes; long[] rumblePattern = (RumbleWaveFormFile == null) ? null : RumbleWaveFormFile.WaveForm.Pattern; int[] lowFreqAmplitude = (RumbleWaveFormFile == null) ? null : RumbleWaveFormFile.WaveForm.LowFrequencyAmplitudes; int[] highFreqAmplitude = (RumbleWaveFormFile == null) ? null : RumbleWaveFormFile.WaveForm.HighFrequencyAmplitudes; MMVibrationManager.AdvancedHapticPattern(iOSString, androidPattern, androidAmplitude, AndroidRepeat, rumblePattern, lowFreqAmplitude, highFreqAmplitude, RumbleRepeat, OldIOSFallback, this); break; case HapticMethods.Continuous: StartCoroutine(ContinuousHapticsCoroutine()); break; case HapticMethods.NativePreset: MMVibrationManager.Haptic(HapticType, false, AllowRumble, this); break; case HapticMethods.Transient: MMVibrationManager.TransientHaptic(TransientIntensity, TransientSharpness, AllowRumble, this); break; case HapticMethods.Stop: if (_continuousPlaying) { MMVibrationManager.StopContinuousHaptic(AllowRumble); _continuousPlaying = false; } break; } }
protected virtual void HandlePower() { _knobValue = Knob.Active ? Knob.Value : 0f; if (!_carStarted) { if ((_knobValue > MinimumKnobValue) && (Knob.Active)) { _carStarted = true; _carStartedAt = Time.time; _lastStartClickAt = Time.time; MMVibrationManager.ContinuousHaptic(_knobValue, _knobValue, MaximumPowerDuration, HapticTypes.MediumImpact, this); CarEngineAudioSource.Play(); } else { Power += Time.deltaTime * ChargingSpeed; Power = Mathf.Clamp(Power, 0f, MaximumPowerDuration); if (Power == MaximumPowerDuration) { Knob.SetActive(true); Knob._rectTransform.localScale = Vector3.one; ReloadingPrompt.SetActive(false); } else { if (!Knob.Active) { Knob.SetValue(CarSpeed); } } } } else { if (Time.time - _carStartedAt > MaximumPowerDuration) { _carStarted = false; Knob.SetActive(false); Knob._rectTransform.localScale = Vector3.one * 0.9f; ReloadingPrompt.SetActive(true); } else { if (_knobValue > MinimumKnobValue) { Power -= Time.deltaTime; Power = Mathf.Clamp(Power, 0f, MaximumPowerDuration); MMVibrationManager.UpdateContinuousHaptic(_knobValue, _knobValue, true); if (Power <= 0f) { _carStarted = false; Knob.SetActive(false); Knob._rectTransform.localScale = Vector3.one * 0.9f; ReloadingPrompt.SetActive(true); } } else { _carStarted = false; _lastStartClickAt = Time.time; MMVibrationManager.StopContinuousHaptic(true); } } } }
/// <summary> /// On Awake, we initialize our iOS haptics. /// Of course, this only needs to be done when on iOS, or targeting iOS. /// A test will be done and this method will do nothing if running on anything else /// </summary> protected virtual void Awake() { MMVibrationManager.iOSInitializeHaptics(); }
/// <summary> /// Triggers the success haptic feedback, a light then heavy vibration on Android, and a success impact on iOS /// </summary> public virtual void TriggerSuccess() { MMVibrationManager.Haptic(HapticTypes.Success); }
/// <summary> /// Triggers the selection haptic feedback, a light vibration on Android, and a light impact on iOS /// </summary> public virtual void TriggerSelection() { MMVibrationManager.Haptic(HapticTypes.Selection); }
/// <summary> /// Triggers the default Vibrate method, which will result in a medium vibration on Android and a medium impact on iOS /// </summary> public virtual void TriggerVibrate() { MMVibrationManager.Vibrate(); }
/// <summary> /// On Disable, we release our iOS haptics (to save memory and avoid garbage). /// Of course, this only needs to be done when on iOS, or targeting iOS. /// A test will be done and this method will do nothing if running on anything else /// </summary> protected virtual void OnDisable() { MMVibrationManager.iOSReleaseHaptics(); }