using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Runtime.InteropServices; using System; using AOT; #if UNITY_IOS using UnityEngine.iOS; #endif namespace MoreMountains.NiceVibrations { /// /// This class handles all iOS haptics specific calls for devices that support the CoreHaptics API (post iOS 13) /// public static class MMNViOSCoreHaptics { public static event Action OnHapticPatternStopped; public static event Action OnHapticPatternError; public static event Action OnHapticPatternReset; private static float _initialContinuousIntensity; private static float _initialContinuousSharpness; #if UNITY_IOS && !UNITY_EDITOR [DllImport("__Internal")] private static extern bool MMNViOS_CoreHapticsSupported(); [DllImport("__Internal")] private static extern void MMNViOS_CreateEngine(); [DllImport("__Internal")] private static extern void MMNViOS_StopEngine(); [DllImport("__Internal")] private static extern void MMNViOS_PlayTransientHapticPattern(float intensity, float sharpness, bool threaded); [DllImport("__Internal")] private static extern void MMNViOS_PlayContinuousHapticPattern(float intensity, float sharpness, float duration, bool threaded); [DllImport("__Internal")] private static extern void MMNViOS_UpdateContinuousHapticPattern(float intensity, float sharpness, bool threaded); [DllImport("__Internal")] private static extern void MMNViOS_StopContinuousHaptic(); [DllImport("__Internal")] private static extern void MMNViOS_PlayCoreHapticsFromJSON(string jsonString, bool threaded); [DllImport("__Internal")] private static extern void MMNViOS_CoreHapticsRegisterHapticEngineFinishedCallback(Action callback); [DllImport("__Internal")] private static extern void MMNViOS_CoreHapticsRegisterHapticEngineErrorCallback(Action callback); [DllImport("__Internal")] private static extern void MMNViOS_CoreHapticsRegisterHapticEngineResetCallback(Action callback); [DllImport("__Internal")] private static extern void MMNViOS_CoreHapticsSetDebugMode(bool status); #else private static bool MMNViOS_CoreHapticsSupported() { return false; } private static void MMNViOS_CoreHapticsSetDebugMode(bool status) { } private static void MMNViOS_CreateEngine() { } private static void MMNViOS_StopEngine() { } private static void MMNViOS_PlayTransientHapticPattern(float intensity, float sharpness, bool threaded) { } private static void MMNViOS_PlayContinuousHapticPattern(float intensity, float sharpness, float duration, bool threaded) { } private static void MMNViOS_UpdateContinuousHapticPattern(float intensity, float sharpness, bool threaded) { } private static void MMNViOS_StopContinuousHaptic() { } private static void MMNViOS_PlayCoreHapticsFromJSON(string jsonString, bool threaded) { } private static void MMNViOS_CoreHapticsRegisterHapticEngineFinishedCallback(Action callback) { } private static void MMNViOS_CoreHapticsRegisterHapticEngineErrorCallback(Action callback) { } private static void MMNViOS_CoreHapticsRegisterHapticEngineResetCallback(Action callback) { } #endif /// /// On construction we initialize our haptic engine /// static MMNViOSCoreHaptics() { MMNViOS_CoreHapticsRegisterHapticEngineFinishedCallback(HapticStoppedCallback); MMNViOS_CoreHapticsRegisterHapticEngineErrorCallback(HapticsErrorCallback); MMNViOS_CoreHapticsRegisterHapticEngineResetCallback(HapticsResetCallback); } /// /// Plays a core haptics pattern from a JSON string that matches the AHAP format /// /// public static void PlayCoreHapticsFromJSON(string jsonString, bool threaded = false) { MMNViOS_PlayCoreHapticsFromJSON(jsonString, threaded); } /// /// Plays a transient haptic pattern of the specified intensity and sharpness. /// Transient haptics are very short haptic patterns. /// /// /// public static void PlayTransientHapticPattern(float intensity, float sharpness, bool threaded = false) { MMNViOS_PlayTransientHapticPattern(intensity, sharpness, threaded); } /// /// Plays a continuous haptic at the specified intensity and sharpness for the specified duration /// The coroutineMonobehaviour parameter is optional, only send that if you intend to raise this specific continuous pattern's intensity higher than its /// initial value /// /// /// /// /// public static void PlayContinuousHapticPattern(float intensity, float sharpness, float duration, MonoBehaviour coroutineMonobehaviour = null, bool threaded = false) { if (intensity < 0.01f) { intensity = 0.01f; } _initialContinuousIntensity = intensity; _initialContinuousSharpness = sharpness; if (coroutineMonobehaviour != null) { _initialContinuousIntensity = 1f; MMNViOS_PlayContinuousHapticPattern(1f, sharpness, duration, threaded); coroutineMonobehaviour.StartCoroutine(ContinuousHapticPatternCoroutine(intensity, sharpness, threaded)); } else { MMNViOS_PlayContinuousHapticPattern(intensity, sharpness, duration, threaded); } } /// /// This coroutine waits for a frame and returns intensity to its normal value /// /// /// /// public static IEnumerator ContinuousHapticPatternCoroutine(float intensity, float sharpness, bool threaded = false) { yield return null; MMNViOS_UpdateContinuousHapticPattern(intensity, sharpness, threaded); } /// /// This method lets you update the intensity and sharpness of a continuous haptics as it's playing. /// Note that this one is a direct implementation of Apple's native method that serves that same purpose, and /// the way it works can be confusing, as instead of just setting the value, it multiplies and adds like so : /// Sending a dynamic parameter for intensity multiplies the original pattern’s event intensity by the dynamic parameter value. /// Sending a dynamic parameter for sharpness adds the dynamic parameter value to the original pattern’s event sharpness. /// If you'd rather not have to do weird maths to just set two values, you can use /// the UpdateContinuousHapticPatternRational method, which will handle that for you. /// /// /// public static void UpdateContinuousHapticPattern(float intensity, float sharpness, bool threaded = false) { MMNViOS_UpdateContinuousHapticPattern(intensity, sharpness, threaded); } /// /// This method lets you update the intensity and sharpness of a continuous haptics as it's playing. /// It simply sets the intensity and sharpness values to the ones set in parameters /// Just note that due to limitations in Apple's implementation, the intensity can't go higher than its initial value. /// The only way to bypass that is to pass a monobehaviour to PlayContinuousHapticPattern when calling it /// /// /// public static void UpdateContinuousHapticPatternRational(float intensity, float sharpness, bool threaded = false) { if (_initialContinuousIntensity < 0.01f) { _initialContinuousIntensity = 0.01f; } float newIntensity = intensity / _initialContinuousIntensity; float newSharpness = sharpness - _initialContinuousSharpness; MMNViOS_UpdateContinuousHapticPattern(newIntensity, newSharpness, threaded); } /// /// Stops all running or continuous haptic patterns /// public static void StopHapticPatterns() { MMNViOS_StopContinuousHaptic(); } /// /// Stops the haptic engine entirely, cutting short any AHAP or transient that may have been playing /// public static void CreateEngine() { MMNViOS_CreateEngine(); } /// /// Stops the haptic engine entirely, cutting short any AHAP or transient that may have been playing /// public static void StopEngine() { MMNViOS_StopEngine(); } /// /// Sets the debug mode to true or false. /// While debug mode is active, pretty much every haptic call will output a log in the console. /// Useful when debugging without a device /// /// public static void SetDebugMode(bool newStatus) { MMNViOS_CoreHapticsSetDebugMode(newStatus); } /// /// Returns true if CoreHaptics are supported, false otherwise /// /// public static bool CoreHapticsSupported() { return MMNViOS_CoreHapticsSupported(); } /// /// Triggers the haptics stopped callback /// #if UNITY_IOS [MonoPInvokeCallback(typeof(Action))] #endif private static void HapticStoppedCallback() { OnHapticPatternStopped?.Invoke(); } /// /// Triggers the haptics error callback /// #if UNITY_IOS [MonoPInvokeCallback(typeof(Action))] #endif private static void HapticsErrorCallback() { OnHapticPatternError?.Invoke(); } /// /// Triggers the haptics reset callback /// #if UNITY_IOS [MonoPInvokeCallback(typeof(Action))] #endif private static void HapticsResetCallback() { OnHapticPatternReset?.Invoke(); } } }