River Jiang
2020-10-27 1f5eda1c9d22a3676298751c7282a5874f13bed0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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
{
    /// <summary>
    /// This class handles all iOS haptics specific calls for devices that support the CoreHaptics API (post iOS 13)
    /// </summary>
    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
 
        /// <summary>
        /// On construction we initialize our haptic engine
        /// </summary>
        static MMNViOSCoreHaptics()
        {
            MMNViOS_CoreHapticsRegisterHapticEngineFinishedCallback(HapticStoppedCallback);
            MMNViOS_CoreHapticsRegisterHapticEngineErrorCallback(HapticsErrorCallback);
            MMNViOS_CoreHapticsRegisterHapticEngineResetCallback(HapticsResetCallback);
        }
 
        /// <summary>
        /// Plays a core haptics pattern from a JSON string that matches the AHAP format
        /// </summary>
        /// <param name="jsonString"></param>
        public static void PlayCoreHapticsFromJSON(string jsonString, bool threaded = false)
        {
            MMNViOS_PlayCoreHapticsFromJSON(jsonString, threaded);
        }
 
        /// <summary>
        /// Plays a transient haptic pattern of the specified intensity and sharpness.
        /// Transient haptics are very short haptic patterns.
        /// </summary>
        /// <param name="intensity"></param>
        /// <param name="sharpness"></param>
        public static void PlayTransientHapticPattern(float intensity, float sharpness, bool threaded = false)
        {
            MMNViOS_PlayTransientHapticPattern(intensity, sharpness, threaded);
        }
 
        /// <summary>
        /// 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
        /// </summary>
        /// <param name="intensity"></param>
        /// <param name="sharpness"></param>
        /// <param name="duration"></param>
        /// <param name="coroutineMonobehaviour"></param>
        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);
            }
        }
 
        /// <summary>
        /// This coroutine waits for a frame and returns intensity to its normal value
        /// </summary>
        /// <param name="intensity"></param>
        /// <param name="sharpness"></param>
        /// <returns></returns>
        public static IEnumerator ContinuousHapticPatternCoroutine(float intensity, float sharpness, bool threaded = false)
        {
            yield return null;
            MMNViOS_UpdateContinuousHapticPattern(intensity, sharpness, threaded);
        }
 
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="intensity"></param>
        /// <param name="sharpness"></param>
        public static void UpdateContinuousHapticPattern(float intensity, float sharpness, bool threaded = false)
        {
            MMNViOS_UpdateContinuousHapticPattern(intensity, sharpness, threaded);
        }
 
        /// <summary>
        /// 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
        /// </summary>
        /// <param name="intensity"></param>
        /// <param name="sharpness"></param>
        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);
        }
 
        /// <summary>
        /// Stops all running or continuous haptic patterns
        /// </summary>
        public static void StopHapticPatterns()
        {
            MMNViOS_StopContinuousHaptic();
        }
 
        /// <summary>
        /// Stops the haptic engine entirely, cutting short any AHAP or transient that may have been playing
        /// </summary>
        public static void CreateEngine()
        {
            MMNViOS_CreateEngine();
        }
 
        /// <summary>
        /// Stops the haptic engine entirely, cutting short any AHAP or transient that may have been playing
        /// </summary>
        public static void StopEngine()
        {
            MMNViOS_StopEngine();
        }
 
        /// <summary>
        /// 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
        /// </summary>
        /// <param name="newStatus"></param>
        public static void SetDebugMode(bool newStatus)
        {
            MMNViOS_CoreHapticsSetDebugMode(newStatus);
        }
 
        /// <summary>
        /// Returns true if CoreHaptics are supported, false otherwise
        /// </summary>
        /// <returns></returns>
        public static bool CoreHapticsSupported()
        {
            return MMNViOS_CoreHapticsSupported();
        }
 
 
        /// <summary>
        /// Triggers the haptics stopped callback
        /// </summary>
        #if UNITY_IOS
        [MonoPInvokeCallback(typeof(Action))]
        #endif
        private static void HapticStoppedCallback()
        {
            OnHapticPatternStopped?.Invoke();
        }
 
        /// <summary>
        /// Triggers the haptics error callback
        /// </summary>
        #if UNITY_IOS
        [MonoPInvokeCallback(typeof(Action))]
        #endif
        private static void HapticsErrorCallback()
        {
            OnHapticPatternError?.Invoke();
        }
 
        /// <summary>
        /// Triggers the haptics reset callback
        /// </summary>
        #if UNITY_IOS
        [MonoPInvokeCallback(typeof(Action))]
        #endif
        private static void HapticsResetCallback()
        {
            OnHapticPatternReset?.Invoke();
        }
    }
}