You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
515 lines
21 KiB
C#
515 lines
21 KiB
C#
3 months ago
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||
|
|
||
|
using System;
|
||
|
using UnityEngine;
|
||
|
using System.Globalization;
|
||
|
|
||
|
namespace Lofelt.NiceVibrations
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// A collection of methods to play simple haptic patterns.
|
||
|
/// </summary>
|
||
|
///
|
||
|
/// Each of the methods here load and play a simple haptic clip or a
|
||
|
/// haptic pattern, depending on the device capabilities.
|
||
|
///
|
||
|
/// None of the methods here are thread-safe and should only be called from
|
||
|
/// the main (Unity) thread. Calling these methods from a secondary thread can
|
||
|
/// cause undefined behaviour and memory leaks.
|
||
|
///
|
||
|
/// After playback has finished, the loaded clips in this class will remain
|
||
|
/// loaded in HapticController.
|
||
|
|
||
|
public static class HapticPatterns
|
||
|
{
|
||
|
static String emphasisTemplate;
|
||
|
static String constantTemplate;
|
||
|
static NumberFormatInfo numberFormat;
|
||
|
static private float[] constantPatternTime = new float[] { 0.0f, 0.0f };
|
||
|
|
||
|
/// <summary>
|
||
|
/// Enum that represents all the types of haptic presets available
|
||
|
/// </summary>
|
||
|
public enum PresetType
|
||
|
{
|
||
|
Selection = 0,
|
||
|
Success = 1,
|
||
|
Warning = 2,
|
||
|
Failure = 3,
|
||
|
LightImpact = 4,
|
||
|
MediumImpact = 5,
|
||
|
HeavyImpact = 6,
|
||
|
RigidImpact = 7,
|
||
|
SoftImpact = 8,
|
||
|
None = -1
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Structure that represents a haptic pattern with amplitude variations.
|
||
|
/// </summary>
|
||
|
///
|
||
|
/// \ref time values have be incremental to be compatible with Preset.
|
||
|
struct Pattern
|
||
|
{
|
||
|
public float[] time;
|
||
|
public float[] amplitude;
|
||
|
|
||
|
static String clipJsonTemplate;
|
||
|
|
||
|
static Pattern()
|
||
|
{
|
||
|
clipJsonTemplate = (Resources.Load("nv-pattern-template") as TextAsset).text;
|
||
|
}
|
||
|
|
||
|
public Pattern(float[] time, float[] amplitude)
|
||
|
{
|
||
|
this.time = time;
|
||
|
this.amplitude = amplitude;
|
||
|
}
|
||
|
|
||
|
// Converts a Pattern to a GamepadRumble
|
||
|
//
|
||
|
// Each pair of adjacent entries in the Pattern create one entry in the GamepadRumble.
|
||
|
public GamepadRumble ToRumble()
|
||
|
{
|
||
|
GamepadRumble result = new GamepadRumble();
|
||
|
if (time.Length <= 1)
|
||
|
{
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Debug.Assert(time.Length == amplitude.Length);
|
||
|
|
||
|
// The first pattern entry needs to have a time of 0.0 for the algorithm below to work
|
||
|
Debug.Assert(time[0] == 0.0f);
|
||
|
|
||
|
int rumbleCount = time.Length - 1;
|
||
|
result.durationsMs = new int[rumbleCount];
|
||
|
result.lowFrequencyMotorSpeeds = new float[rumbleCount];
|
||
|
result.highFrequencyMotorSpeeds = new float[rumbleCount];
|
||
|
result.totalDurationMs = 0;
|
||
|
for (int rumbleIndex = 0; rumbleIndex < rumbleCount; rumbleIndex++)
|
||
|
{
|
||
|
int patternDurationMs = (int)((time[rumbleIndex + 1] - time[rumbleIndex]) * 1000.0f);
|
||
|
result.durationsMs[rumbleIndex] = patternDurationMs;
|
||
|
result.lowFrequencyMotorSpeeds[rumbleIndex] = amplitude[rumbleIndex];
|
||
|
result.highFrequencyMotorSpeeds[rumbleIndex] = amplitude[rumbleIndex];
|
||
|
result.totalDurationMs += result.durationsMs[rumbleIndex];
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Converts a Pattern to a haptic clip JSON string.
|
||
|
public String ToClip()
|
||
|
{
|
||
|
if (clipJsonTemplate == null)
|
||
|
{
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
String amplitudeEnvelope = "";
|
||
|
for (int i = 0; i < time.Length; i++)
|
||
|
{
|
||
|
float clampedAmplitude = Mathf.Clamp(amplitude[i], 0.0f, 1.0f);
|
||
|
amplitudeEnvelope += "{ \"time\":" + time[i].ToString(numberFormat) + "," +
|
||
|
"\"amplitude\":" + clampedAmplitude.ToString(numberFormat) + "}";
|
||
|
|
||
|
// Don't add a comma to the JSON data if we're at the end of the envelope
|
||
|
if (i + 1 < time.Length)
|
||
|
{
|
||
|
amplitudeEnvelope += ",";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return clipJsonTemplate.Replace("{amplitude-envelope}", amplitudeEnvelope);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// A haptic preset in its different representations
|
||
|
//
|
||
|
// A Preset has four different representations, as there are four different playback methods.
|
||
|
// Each representation is created at construction time, so that playing a
|
||
|
// Preset has no further conversion cost at playback time.
|
||
|
internal struct Preset
|
||
|
{
|
||
|
// For playback on iOS, using system haptics
|
||
|
public PresetType type;
|
||
|
|
||
|
// For playback on Android devices without amplitude control
|
||
|
public float[] maximumAmplitudePattern;
|
||
|
|
||
|
// For playback on Android devices with amplitude control
|
||
|
public byte[] jsonClip;
|
||
|
|
||
|
// For playback on gamepads
|
||
|
#if ((!UNITY_ANDROID && !UNITY_IOS) || UNITY_EDITOR) && NICE_VIBRATIONS_INPUTSYSTEM_INSTALLED && ENABLE_INPUT_SYSTEM && !NICE_VIBRATIONS_DISABLE_GAMEPAD_SUPPORT
|
||
|
public GamepadRumble gamepadRumble;
|
||
|
#endif
|
||
|
|
||
|
public Preset(PresetType type, float[] time, float[] amplitude)
|
||
|
{
|
||
|
Debug.Assert(type != PresetType.None);
|
||
|
Pattern pattern = new Pattern(time, amplitude);
|
||
|
this.type = type;
|
||
|
this.maximumAmplitudePattern = pattern.time;
|
||
|
#if ((!UNITY_ANDROID && !UNITY_IOS) || UNITY_EDITOR) && NICE_VIBRATIONS_INPUTSYSTEM_INSTALLED && ENABLE_INPUT_SYSTEM && !NICE_VIBRATIONS_DISABLE_GAMEPAD_SUPPORT
|
||
|
this.gamepadRumble = pattern.ToRumble();
|
||
|
#endif
|
||
|
this.jsonClip = System.Text.Encoding.UTF8.GetBytes(pattern.ToClip());
|
||
|
}
|
||
|
|
||
|
public float GetDuration()
|
||
|
{
|
||
|
if (maximumAmplitudePattern.Length > 0)
|
||
|
{
|
||
|
return maximumAmplitudePattern[maximumAmplitudePattern.Length - 1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 0f;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Selection" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Selection;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Light" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Light;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Medium" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Medium;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Heavy" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Heavy;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Rigid" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Rigid;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Soft" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Soft;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Success" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Success;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Failure" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Failure;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Predefined Preset that represents a "Warning" haptic preset
|
||
|
/// </summary>
|
||
|
internal static Preset Warning;
|
||
|
|
||
|
static HapticPatterns()
|
||
|
{
|
||
|
emphasisTemplate = (Resources.Load("nv-emphasis-template") as TextAsset).text;
|
||
|
constantTemplate = (Resources.Load("nv-constant-template") as TextAsset).text;
|
||
|
|
||
|
numberFormat = new NumberFormatInfo();
|
||
|
numberFormat.NumberDecimalSeparator = ".";
|
||
|
|
||
|
// Initialize presets after setting the number format, so that the correct decimal
|
||
|
// separator is used when building the JSON representation.
|
||
|
|
||
|
Selection = new Preset(PresetType.Selection, new float[] { 0.0f, 0.04f },
|
||
|
new float[] { 0.471f, 0.471f });
|
||
|
|
||
|
Light = new Preset(PresetType.LightImpact, new float[] { 0.000f, 0.040f },
|
||
|
new float[] { 0.156f, 0.156f });
|
||
|
|
||
|
Medium = new Preset(PresetType.MediumImpact, new float[] { 0.000f, 0.080f },
|
||
|
new float[] { 0.471f, 0.471f });
|
||
|
|
||
|
Heavy = new Preset(PresetType.HeavyImpact, new float[] { 0.0f, 0.16f },
|
||
|
new float[] { 1.0f, 1.00f });
|
||
|
|
||
|
Rigid = new Preset(PresetType.RigidImpact, new float[] { 0.0f, 0.04f },
|
||
|
new float[] { 1.0f, 1.00f });
|
||
|
|
||
|
Soft = new Preset(PresetType.SoftImpact, new float[] { 0.000f, 0.160f },
|
||
|
new float[] { 0.156f, 0.156f });
|
||
|
|
||
|
Success = new Preset(PresetType.Success, new float[] { 0.0f, 0.040f, 0.080f, 0.240f },
|
||
|
new float[] { 0.0f, 0.157f, 0.000f, 1.000f });
|
||
|
|
||
|
Failure = new Preset(PresetType.Failure,
|
||
|
new float[] { 0.0f, 0.080f, 0.120f, 0.200f, 0.240f, 0.400f, 0.440f, 0.480f },
|
||
|
new float[] { 0.0f, 0.470f, 0.000f, 0.470f, 0.000f, 1.000f, 0.000f, 0.157f });
|
||
|
|
||
|
Warning = new Preset(PresetType.Warning, new float[] { 0.0f, 0.120f, 0.240f, 0.280f },
|
||
|
new float[] { 0.0f, 1.000f, 0.000f, 0.470f });
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Plays a single emphasis point.
|
||
|
/// </summary>
|
||
|
///
|
||
|
/// Plays a haptic clip that consists only of one breakpoint with emphasis.
|
||
|
/// On iOS, this translates to a transient, and on Android and gamepads to
|
||
|
/// a quick vibration.
|
||
|
///
|
||
|
/// <param name="amplitude">The amplitude of the emphasis, from 0.0 to 1.0</param>
|
||
|
/// <param name="frequency">The frequency of the emphasis, from 0.0 to 1.0</param>
|
||
|
public static void PlayEmphasis(float amplitude, float frequency)
|
||
|
{
|
||
|
if (emphasisTemplate == null || !HapticController.hapticsEnabled)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Use HapticController.Play() to play a .haptic clip on mobile devices
|
||
|
// that support it, or to play a gamepad rumble if a gamepad is connected.
|
||
|
if (HapticController.Init() || GamepadRumbler.IsConnected())
|
||
|
{
|
||
|
float clampedAmplitude = Mathf.Clamp(amplitude, 0.0f, 1.0f);
|
||
|
float clampedFrequency = Mathf.Clamp(frequency, 0.0f, 1.0f);
|
||
|
const float duration = 0.1f;
|
||
|
|
||
|
String json = emphasisTemplate
|
||
|
.Replace("{amplitude}", clampedAmplitude.ToString(numberFormat))
|
||
|
.Replace("{frequency}", clampedFrequency.ToString(numberFormat))
|
||
|
.Replace("{duration}", duration.ToString(numberFormat));
|
||
|
|
||
|
// This preprocessor section will only run for non-mobile platforms
|
||
|
GamepadRumble rumble = new GamepadRumble();
|
||
|
#if ((!UNITY_ANDROID && !UNITY_IOS) || UNITY_EDITOR) && NICE_VIBRATIONS_INPUTSYSTEM_INSTALLED && ENABLE_INPUT_SYSTEM && !NICE_VIBRATIONS_DISABLE_GAMEPAD_SUPPORT
|
||
|
rumble.durationsMs = new int[] { (int)(duration * 1000) };
|
||
|
rumble.lowFrequencyMotorSpeeds = new float[] { clampedAmplitude };
|
||
|
rumble.highFrequencyMotorSpeeds = new float[] { clampedFrequency };
|
||
|
#endif
|
||
|
|
||
|
HapticController.Load(System.Text.Encoding.UTF8.GetBytes(json), rumble);
|
||
|
HapticController.Loop(false);
|
||
|
HapticController.Play();
|
||
|
}
|
||
|
|
||
|
// As a fallback, play a short buzz on Android, or a preset on iOS.
|
||
|
else if (DeviceCapabilities.isVersionSupported)
|
||
|
{
|
||
|
#if (UNITY_ANDROID && !UNITY_EDITOR)
|
||
|
LofeltHaptics.PlayMaximumAmplitudePattern(new float[]{ 0.0f, 0.05f });
|
||
|
#elif (UNITY_IOS && !UNITY_EDITOR)
|
||
|
PresetType preset = presetTypeForEmphasis(amplitude);
|
||
|
LofeltHaptics.TriggerPresetHaptics((int)preset);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Automatically selects the fallback preset based on the emphasis point amplitude.
|
||
|
/// </summary>
|
||
|
///
|
||
|
/// <param name="amplitude">The amplitude of the emphasis, from 0.0 to 1.0</param>
|
||
|
static PresetType presetTypeForEmphasis(float amplitude)
|
||
|
{
|
||
|
if (amplitude > 0.5f)
|
||
|
{
|
||
|
return HapticPatterns.PresetType.HeavyImpact;
|
||
|
}
|
||
|
else if (amplitude <= 0.5f && amplitude > 0.3)
|
||
|
{
|
||
|
return HapticPatterns.PresetType.MediumImpact;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return HapticPatterns.PresetType.LightImpact;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Plays a haptic with constant amplitude and frequency.
|
||
|
/// </summary>
|
||
|
///
|
||
|
/// On iOS and with gamepads, you can use HapticController::clipLevel to modulate the haptic
|
||
|
/// while it is playing. iOS additional supports modulating the frequency with
|
||
|
/// HapticController::clipFrequencyShift.
|
||
|
///
|
||
|
/// When \ref DeviceCapabilities.meetsAdvancedRequirements returns false on mobile,
|
||
|
/// the behavior of this method is different for iOS and Android:
|
||
|
/// <ul>
|
||
|
/// <li>On iOS, it will play the preset <c>HapticPatterns.PresetType.HeavyImpact</c>. </li>
|
||
|
///
|
||
|
/// <li>On Android, it will play a pattern with maximum amplitude for the set <c>duration</c>
|
||
|
/// since there is no amplitude control.</li>
|
||
|
///
|
||
|
/// </ul>
|
||
|
/// <param name="amplitude">Amplitude, from 0.0 to 1.0</param>
|
||
|
/// <param name="frequency">Frequency, from 0.0 to 1.0</param>
|
||
|
/// <param name="duration">Play duration in seconds</param>
|
||
|
public static void PlayConstant(float amplitude, float frequency, float duration)
|
||
|
{
|
||
|
if (constantTemplate == null || !HapticController.hapticsEnabled)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float clampedAmplitude = Mathf.Clamp(amplitude, 0.0f, 1.0f);
|
||
|
float clampedFrequency = Mathf.Clamp(frequency, 0.0f, 1.0f);
|
||
|
float clampedDurationSecs = Mathf.Max(duration, 0.0f);
|
||
|
|
||
|
String json = constantTemplate
|
||
|
.Replace("{duration}", clampedDurationSecs.ToString(numberFormat));
|
||
|
|
||
|
// This preprocessor section will only run for non-mobile platforms
|
||
|
GamepadRumble rumble = new GamepadRumble();
|
||
|
#if ((!UNITY_ANDROID && !UNITY_IOS) || UNITY_EDITOR) && NICE_VIBRATIONS_INPUTSYSTEM_INSTALLED && ENABLE_INPUT_SYSTEM && !NICE_VIBRATIONS_DISABLE_GAMEPAD_SUPPORT
|
||
|
int rumbleDurationMs = (int)(clampedDurationSecs * 1000);
|
||
|
const int rumbleEntryDurationMs = 16; // One rumble entry per frame at 60 FPS, which is the limit of what GamepadRumbler can play
|
||
|
int rumbleEntryCount = rumbleDurationMs / rumbleEntryDurationMs;
|
||
|
rumble.durationsMs = new int[rumbleEntryCount];
|
||
|
rumble.lowFrequencyMotorSpeeds = new float[rumbleEntryCount];
|
||
|
rumble.highFrequencyMotorSpeeds = new float[rumbleEntryCount];
|
||
|
|
||
|
// Create many rumble entries instead of just one. With just one entry, changing
|
||
|
// clipLevel while the rumble is playing would have no effect, as GamepadRumbler applies
|
||
|
// a change only to the next rumble entry, not the one currently playing.
|
||
|
for (int i = 0; i < rumbleEntryCount; i++)
|
||
|
{
|
||
|
rumble.durationsMs[i] = rumbleEntryDurationMs;
|
||
|
rumble.lowFrequencyMotorSpeeds[i] = 1.0f;
|
||
|
rumble.highFrequencyMotorSpeeds[i] = 1.0f;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (HapticController.Init() || GamepadRumbler.IsConnected())
|
||
|
{
|
||
|
HapticController.Load(System.Text.Encoding.UTF8.GetBytes(json), rumble);
|
||
|
HapticController.Loop(false);
|
||
|
HapticController.clipLevel = clampedAmplitude;
|
||
|
HapticController.clipFrequencyShift = clampedFrequency;
|
||
|
HapticController.Play();
|
||
|
}
|
||
|
else if (DeviceCapabilities.isVersionSupported)
|
||
|
{
|
||
|
#if (UNITY_ANDROID && !UNITY_EDITOR)
|
||
|
constantPatternTime[1] = duration;
|
||
|
LofeltHaptics.PlayMaximumAmplitudePattern(constantPatternTime);
|
||
|
#elif (UNITY_IOS && !UNITY_EDITOR)
|
||
|
HapticPatterns.PlayPreset(PresetType.HeavyImpact);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static Preset GetPresetForType(PresetType type)
|
||
|
{
|
||
|
Debug.Assert(type != PresetType.None);
|
||
|
|
||
|
switch (type)
|
||
|
{
|
||
|
case PresetType.Selection:
|
||
|
return Selection;
|
||
|
case PresetType.LightImpact:
|
||
|
return Light;
|
||
|
case PresetType.MediumImpact:
|
||
|
return Medium;
|
||
|
case PresetType.HeavyImpact:
|
||
|
return Heavy;
|
||
|
case PresetType.RigidImpact:
|
||
|
return Rigid;
|
||
|
case PresetType.SoftImpact:
|
||
|
return Soft;
|
||
|
case PresetType.Success:
|
||
|
return Success;
|
||
|
case PresetType.Failure:
|
||
|
return Failure;
|
||
|
case PresetType.Warning:
|
||
|
return Warning;
|
||
|
}
|
||
|
|
||
|
// Silence compiler warning about not all code paths returning something
|
||
|
return Medium;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Plays a set of predefined haptic patterns.
|
||
|
/// </summary>
|
||
|
///
|
||
|
/// These predefined haptic patterns are played and represented in different ways for iOS,
|
||
|
/// Android and gamepads.
|
||
|
///
|
||
|
/// - On iOS, this function triggers system haptics that are native to iOS. Calling
|
||
|
/// \ref HapticController.Stop() won't stop haptics.
|
||
|
/// - On Android devices that can play <c>.haptic</c> clips (DeviceCapabilities.meetsAdvancedRequirements
|
||
|
/// is <c>true</c>) and on gamepads, this function plays a haptic pattern that has a similar
|
||
|
/// experience to the matching iOS system haptics.
|
||
|
/// - On Android devices that can not play <c>.haptic</c> clips (DeviceCapabilities.meetsAdvancedRequirements
|
||
|
/// is <c>false</c>), this function plays a haptic pattern that has a similar experience to
|
||
|
/// the matching iOS system haptics, by turning the motor off and on at maximum amplitude.
|
||
|
///
|
||
|
/// This is a "fire-and-forget" method. Other functionalities like seeking, looping, and
|
||
|
/// runtime modulation won't work after calling this method.
|
||
|
///
|
||
|
/// <param name="presetType">Type of preset represented by a \ref PresetType enum</param>
|
||
|
public static void PlayPreset(PresetType presetType)
|
||
|
{
|
||
|
if (!HapticController.hapticsEnabled || presetType == PresetType.None)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Preset preset = GetPresetForType(presetType);
|
||
|
|
||
|
#if (UNITY_IOS && !UNITY_EDITOR)
|
||
|
LofeltHaptics.TriggerPresetHaptics((int)presetType);
|
||
|
return;
|
||
|
#else
|
||
|
if (HapticController.Init() || GamepadRumbler.IsConnected())
|
||
|
{
|
||
|
#if ((!UNITY_ANDROID && !UNITY_IOS) || UNITY_EDITOR) && NICE_VIBRATIONS_INPUTSYSTEM_INSTALLED && ENABLE_INPUT_SYSTEM && !NICE_VIBRATIONS_DISABLE_GAMEPAD_SUPPORT
|
||
|
HapticController.Load(preset.jsonClip, preset.gamepadRumble);
|
||
|
#else
|
||
|
HapticController.Load(preset.jsonClip);
|
||
|
#endif
|
||
|
HapticController.Loop(false);
|
||
|
HapticController.Play();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (DeviceCapabilities.isVersionSupported)
|
||
|
{
|
||
|
#if (UNITY_ANDROID && !UNITY_EDITOR)
|
||
|
LofeltHaptics.PlayMaximumAmplitudePattern(preset.maximumAmplitudePattern);
|
||
|
return;
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the haptic preset duration.
|
||
|
/// </summary>
|
||
|
///
|
||
|
/// While a preset is played back in different ways on iOS, Android and gamepads, the
|
||
|
/// duration is similar for each playback method.
|
||
|
///
|
||
|
/// <param name="presetType"> Type of preset represented by a \ref PresetType enum </param>
|
||
|
/// <returns>Returns a float with a the preset duration; if the selected preset is `None`, it returns 0</returns>
|
||
|
public static float GetPresetDuration(PresetType presetType)
|
||
|
{
|
||
|
if (presetType == PresetType.None)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return GetPresetForType(presetType).GetDuration();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|