SamsonGame/Assets/Sources/Feel/MMFeedbacks/MMFeedbacksForThirdParty/NiceVibrations/Feedbacks/MMFeedbackHaptics.cs

298 lines
16 KiB
C#
Raw Normal View History

2021-12-29 20:50:11 +03:00
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MoreMountains.Feedbacks;
#if MOREMOUNTAINS_NICEVIBRATIONS
using MoreMountains.NiceVibrations;
namespace MoreMountains.FeedbacksForThirdParty
{
/// <summary>
/// Add this feedback to be able to trigger haptic feedbacks via the NiceVibration library.
/// It'll let you create transient or continuous vibrations, play presets or advanced patterns via AHAP files, and stop any vibration at any time
/// </summary>
[AddComponentMenu("")]
[FeedbackPath("Haptics")]
[FeedbackHelp("This feedback lets you trigger haptic feedbacks through the Nice Vibrations asset, available on the Unity Asset Store. You'll need to own that asset and have it " +
"in your project for this to work.")]
public class MMFeedbackHaptics : MMFeedback
{
/// the possible haptic methods for this feedback
public enum HapticMethods { NativePreset, Transient, Continuous, AdvancedPattern, Stop, AdvancedTransient, AdvancedContinuous }
/// the timescale to operate on
public enum Timescales { ScaledTime, UnscaledTime }
// NATIVE PRESET -----------------------------------------------------------------------------------------------------
[Header("Haptics")]
/// the method to use when triggering this haptic feedback
[Tooltip("the method to use when triggering this haptic feedback")]
public HapticMethods HapticMethod = HapticMethods.NativePreset;
/// the type of native preset to use
[Tooltip("the type of native preset to use")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.NativePreset)]
public HapticTypes HapticType = HapticTypes.None;
// TRANSIENT ---------------------------------------------------------------------------------------------------------
/// the intensity of the transient haptic
[Tooltip("the intensity of the transient haptic")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.Transient)]
public float TransientIntensity = 1f;
/// the sharpness of the transient haptic
[Tooltip("the sharpness of the transient haptic")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.Transient)]
public float TransientSharpness = 1f;
// ADV TRANSIENT ---------------------------------------------------------------------------------------------------------
/// whether or not to vibrate on iOS when in AdvancedTransient mode
[Tooltip("whether or not to vibrate on iOS when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public bool ATVibrateIOS = true;
/// the intensity on iOS when in AdvancedTransient mode
[Tooltip("the intensity on iOS when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public float ATIOSIntensity = 1f;
/// the sharpness on iOS when in AdvancedTransient mode
[Tooltip("the sharpness on iOS when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public float ATIOSSharpness = 1f;
/// whether or not to vibrate on android when in AdvancedTransient mode
[Tooltip("whether or not to vibrate on android when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public bool ATVibrateAndroid = true;
/// whether or not to vibrate on android if no support for advanced vibrations when in AdvancedTransient mode
[Tooltip("whether or not to vibrate on android if no support for advanced vibrations when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public bool ATVibrateAndroidIfNoSupport = false;
/// the intensity on android when in AdvancedTransient mode
[Tooltip("the intensity on android when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public float ATAndroidIntensity = 1f;
/// the sharpness on android when in AdvancedTransient mode
[Tooltip("the sharpness on android when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public float ATAndroidSharpness = 1f;
/// whether or not to rumble when in AdvancedTransient mode
[Tooltip("whether or not to rumble when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public bool ATRumble = true;
/// the rumble intensity when in AdvancedTransient mode
[Tooltip("the rumble intensity when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public float ATRumbleIntensity = 1f;
/// the rumble sharpness when in AdvancedTransient mode
[Tooltip("the rumble sharpness when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public float ATRumbleSharpness = 1f;
/// the controllerID when in AdvancedTransient mode
[Tooltip("the controllerID when in AdvancedTransient mode")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedTransient)]
public int ATRumbleControllerID = -1;
// CONTINUOUS ---------------------------------------------------------------------------------------------------------
/// the intensity that should be used to initialize the continuous haptic
[Tooltip("the intensity that should be used to initialize the continuous haptic")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.Continuous)]
public float InitialContinuousIntensity = 1f;
/// the curve used to tween the continuous intensity
[Tooltip("the curve used to tween the continuous intensity")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.Continuous)]
public AnimationCurve ContinuousIntensityCurve = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1f, 1f));
/// the sharpness that should be used to initialize the continuous haptic
[Tooltip("the sharpness that should be used to initialize the continuous haptic")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.Continuous)]
public float InitialContinuousSharpness = 1f;
/// the curve used to tween the continuous sharpness
[Tooltip("the curve used to tween the continuous sharpness")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.Continuous)]
public AnimationCurve ContinuousSharpnessCurve = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1f, 1f));
/// the duration of the continuous haptic
[Tooltip("the duration of the continuous haptic")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.Continuous)]
public float ContinuousDuration = 1f;
// ADV PATTERN ---------------------------------------------------------------------------------------------------------
/// whether or not to trigger advanced patterns on iOS
[Tooltip("whether or not to trigger advanced patterns on iOS")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public bool APVibrateIOS = true;
/// the AHAP file to use to trigger a pattern on iOS
[Tooltip("the AHAP file to use to trigger a pattern on iOS")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public TextAsset AHAPFileForIOS;
/// whether or not to trigger advanced patterns on Android
[Tooltip("whether or not to trigger advanced patterns on Android")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public bool APVibrateAndroid = true;
/// whether or not to vibrate if there's no haptics support
[Tooltip("whether or not to vibrate if there's no haptics support")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public bool APVibrateAndroidIfNoSupport = false;
/// the WaveFormFile to use to trigger a pattern on Android
[Tooltip("the WaveFormFile to use to trigger a pattern on Android")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public MMNVAndroidWaveFormAsset AndroidWaveFormFile;
/// whether or not to trigger advanced patterns on rumble
[Tooltip("whether or not to trigger advanced patterns on rumble")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public bool APRumble = true;
/// the file to use to trigger a rumble on gamepad
[Tooltip("the file to use to trigger a rumble on gamepad")]
[MMNVEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public MMNVRumbleWaveFormAsset RumbleWaveFormFile;
/// the amount of times this should repeat on Android (-1 : zero, 0 : infinite, 1 : one time, 2 : twice, etc)
[Tooltip("the amount of times this should repeat on Android (-1 : zero, 0 : infinite, 1 : one time, 2 : twice, etc)")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public int AndroidRepeat = -1;
/// the amount of times this should repeat on gamepad
[Tooltip("the amount of times this should repeat on gamepad")]
[MMNVEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public int RumbleRepeat = -1;
/// a haptic type to play on older iOS APIs (prior to iOS 13)
[Tooltip("a haptic type to play on older iOS APIs (prior to iOS 13)")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public HapticTypes OldIOSFallback;
/// whether to run this on scaled or unscaled time
[Tooltip("whether to run this on scaled or unscaled time")]
[MMFEnumCondition("HapticMethod", (int)HapticMethods.AdvancedPattern)]
public Timescales Timescale = Timescales.UnscaledTime;
// RUMBLE -------------------------------------------------------------------------------------------------------------
[Header("Rumble")]
/// whether or not this feedback should trigger a rumble on gamepad
[Tooltip("whether or not this feedback should trigger a rumble on gamepad")]
public bool AllowRumble = true;
/// the ID of the controller to rumble (-1 : auto/current, 0 : first controller, 1 : second controller, etc)
[Tooltip("the ID of the controller to rumble (-1 : auto/current, 0 : first controller, 1 : second controller, etc)")]
public int ControllerID = -1;
protected static bool _continuousPlaying = false;
protected static float _continuousStartedAt = 0f;
/// <summary>
/// When this feedback gets played
/// </summary>
/// <param name="position"></param>
/// <param name="feedbacksIntensity"></param>
protected override void CustomPlayFeedback(Vector3 position, float feedbacksIntensity = 1.0f)
{
if (!Active)
{
return;
}
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(APVibrateIOS, iOSString, APVibrateAndroid, androidPattern, androidAmplitude,
AndroidRepeat, APVibrateAndroidIfNoSupport, APRumble,
rumblePattern, lowFreqAmplitude, highFreqAmplitude, RumbleRepeat,
OldIOSFallback, this, ControllerID);
break;
case HapticMethods.Continuous:
StartCoroutine(ContinuousHapticsCoroutine());
break;
case HapticMethods.NativePreset:
MMVibrationManager.Haptic(HapticType, false, AllowRumble, this, ControllerID);
break;
case HapticMethods.Transient:
MMVibrationManager.TransientHaptic(TransientIntensity, TransientSharpness, AllowRumble, this, ControllerID);
break;
case HapticMethods.AdvancedTransient:
MMVibrationManager.TransientHaptic(ATVibrateIOS, ATIOSIntensity, ATIOSSharpness, ATVibrateAndroid,
ATAndroidIntensity, ATAndroidSharpness, ATVibrateAndroidIfNoSupport, ATRumble,
ATRumbleIntensity, ATRumbleSharpness, ATRumbleControllerID, this);
break;
case HapticMethods.AdvancedContinuous:
break;
case HapticMethods.Stop:
if (_continuousPlaying)
{
MMVibrationManager.StopContinuousHaptic(AllowRumble);
_continuousPlaying = false;
}
break;
}
}
/// <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, ControllerID);
#endif
}
yield return null;
}
if (_continuousPlaying)
{
_continuousPlaying = false;
MMVibrationManager.StopContinuousHaptic(AllowRumble);
}
}
/// <summary>
/// This methods computes and returns the elapsed time since the start of the last played continuous haptic
/// </summary>
/// <returns></returns>
protected virtual float ComputeElapsedTime()
{
float elapsedTime = (Timescale == Timescales.ScaledTime) ? Time.time - _continuousStartedAt : Time.unscaledTime - _continuousStartedAt;
return elapsedTime;
}
/// <summary>
/// Remaps value x from AB to CD
/// </summary>
/// <param name="x"></param>
/// <param name="A"></param>
/// <param name="B"></param>
/// <param name="C"></param>
/// <param name="D"></param>
/// <returns></returns>
public static float Remap(float x, float A, float B, float C, float D)
{
float remappedValue = C + (x - A) / (B - A) * (D - C);
return remappedValue;
}
}
}
#endif