Interhaptics SDK for Unity 1.6
Loading...
Searching...
No Matches
HapticSource.cs
Go to the documentation of this file.
1/* ​
2* Copyright (c) 2024 Go Touch VR SAS. All rights reserved. ​
3* ​
4*/
5
6using UnityEngine;
7using System.Collections.Generic;
10using System.Collections;
11using System;
12using System.IO;
13
15{
16 [AddComponentMenu("Interhaptics/HapticSource")]
17 public class HapticSource : MonoBehaviour
18 {
19 [Header("Haptic Effect File")]
20 [SerializeField]
22 [Tooltip("Use the StreamingAssets path to load the haptic effect file.")]
23 [SerializeField]
24 public bool useStreamingAssets = false; // Boolean to activate the StreamingAssets path
25 [SerializeField]
26 [Tooltip("Path to the haptic effect file in the StreamingAssets folder.")]
28 [Header("Haptic Source Settings")]
29 [Range(0, 2)]
30 [SerializeField]
31 private float sourceIntensity = 1.0f;
32 [Tooltip("Indicates the delay before playing the vibration.")]
33 [SerializeField]
34 public float vibrationOffset;
35 private float textureOffset;
36 private float stiffnessOffset;
37 [SerializeField]
38 public bool debugMode;
39 [Tooltip("Indicates whether the effect should loop.")]
40 [SerializeField]
41 public bool isLooping;
42 [ConditionalHide("isLooping", true)]
43 [Tooltip("Maximum number of loops")] //TODO: -1 means no limit
44 [SerializeField]
45 public int maxLoops = 1;
46 [ConditionalHide("isLooping", true)]
47 [Tooltip("Maximum time for loops")]
48 [SerializeField]
49 public float maxLoopTime = 10f;
50 [SerializeField]
51 public double targetIntensity = 1.0;
52 [SerializeField]
53 public bool playAtStart = false;
54 public bool isPlaying = false;
55 [HideInInspector]
56 public double hapticEffectDuration = 0;
57 private float currentSourceIntensity;
58
59 // Fields necessary for the loop control
60 public Coroutine playingCoroutine = null;
61 [HideInInspector]
62 public double loopStartTime = 0;
63
64 public const string ERROR_MESSAGE_MONO = "Interhaptics requires IL2CPP scripting backend for Android. Please change it in Player Settings. Haptics will not play on the Mono scripting backend on the Android platform.";
66 {
67 get; set;
68 }
69
70 // Public property to get and set the source intensity
71 public float SourceIntensity
72 {
73 get => sourceIntensity;
74 set
75 {
76 sourceIntensity = value;
77 DebugMode("Source intensity set to: " + sourceIntensity);
78 ApplySourceIntensity(); // Apply the intensity immediately when set
79 }
80 }
81
82 #region Lifecycle
86 protected virtual void Awake()
87 {
88#if UNITY_ANDROID
89 if (HapticManager.MonoScriptingBackend)
90 {
91 DebugMode(ERROR_MESSAGE_MONO + "Haptic Source");
92 return;
93 }
94 else
95 {
96 DebugMode("IL2CPP Haptic Source");
97 }
98#endif
100 {
101 hapticEffectStreamingAssetsPath = Path.Combine(Application.streamingAssetsPath, hapticEffectStreamingAssetsPath);
102 if (File.Exists(hapticEffectStreamingAssetsPath))
103 {
104 HapticMaterialId = Core.HAR.AddHMString(File.ReadAllText(hapticEffectStreamingAssetsPath));
105 DebugMode("Loaded haptic material from StreamingAssets: " + hapticEffectStreamingAssetsPath);
106 }
107 else
108 {
109 DebugMode("Haptic material file not found in StreamingAssets: " + hapticEffectStreamingAssetsPath);
110 }
111 }
112 else if (hapticMaterial != null)
113 {
114 HapticMaterialId = Core.HAR.AddHM(hapticMaterial);
115 }
116 else
117 {
118 DebugMode("No haptic effect provided. Please assign a HapticMaterial in the inspector or provide a path to a haptic effect file in the StreamingAssets directory.");
119 }
120 }
121
125 protected virtual void Start()
126 {
127 // Initialize the haptic effect at the start of the game
128 hapticEffectDuration = HAR.GetVibrationLength(HapticMaterialId);
129 currentSourceIntensity = sourceIntensity;
130 if (hapticMaterial!=null)
131 {
132 DebugMode("Haptic effect duration: " + hapticEffectDuration + " " + hapticMaterial.name);
133 }
134 else
135 {
136 DebugMode("Parametric Haptic effect duration: " + hapticEffectDuration + " " + HapticMaterialId);
137 }
138 if (isLooping)
139 {
140 float maxComputedTimeInLoops = maxLoops * (float)hapticEffectDuration;
141 if (maxComputedTimeInLoops < maxLoopTime)
142 {
143 maxLoopTime = maxComputedTimeInLoops;
144 }
145 else
146 {
147 maxLoops = (int)(maxLoopTime / (float)hapticEffectDuration);
149 }
150 }
151 else
152 {
154 maxLoops = 1;
155 }
156 if (playAtStart)
157 {
159 }
160 }
161
165 protected virtual void Update()
166 {
167 if (sourceIntensity != currentSourceIntensity)
168 {
170 currentSourceIntensity = sourceIntensity;
171 DebugMode("Source intensity changed to: " + sourceIntensity);
172 }
173 }
174
175#endregion
176
177 public virtual void ApplyTargetIntensity()
178 {
179 //Empty for now
180 }
181
186 {
187 HAR.SetEventIntensity(HapticMaterialId, sourceIntensity);
188 }
192 public void ApplyLooping(int loopValue)
193 {
194 DebugMode("Applied looping: " + loopValue);
195 HAR.SetEventLoop(HapticMaterialId, loopValue);
196 }
197
202 public void DebugMode(string debugMessage)
203 {
204 if (debugMode)
205 {
206 Debug.Log(debugMessage);
207 }
208 }
209
213 public virtual void Play()
214 {
215 isPlaying = true;
216 Core.HAR.PlayEvent(HapticMaterialId, -Time.realtimeSinceStartup - vibrationOffset, textureOffset, stiffnessOffset);
217 }
218
222 public virtual void Stop()
223 {
224 isPlaying = false;
225 Core.HAR.StopEvent(HapticMaterialId);
226#if (UNITY_ANDROID && !ENABLE_METAQUEST) || UNITY_IOS && !UNITY_EDITOR
227 HAR.StopAllEvents();
228#endif
229 if (playingCoroutine != null)
230 {
231 StopCoroutine(playingCoroutine);
232 playingCoroutine = null;
233 }
234 }
235
240 public void AddTarget(List<HapticBodyMapping.CommandData> Target)
241 {
242 Core.HAR.AddTargetToEvent(HapticMaterialId, Target);
243 }
244
249 public void RemoveTarget(List<HapticBodyMapping.CommandData> Target)
250 {
251 Core.HAR.RemoveTargetFromEvent(HapticMaterialId, Target);
252 }
256 public virtual void PlayEventVibration()
257 {
258#if (!ENABLE_METAQUEST && !ENABLE_OPENXR && UNITY_ANDROID && !UNITY_EDITOR) || UNITY_IOS // Coroutine logic specific to Android and iOS
259 float loopStartTime = Time.time;
260 int maxComputedLoops = maxLoops > 0 ? maxLoops : int.MaxValue;
261 DebugMode("Playing Haptic Material id:" + HapticMaterialId);
262 HAR.PlayHapticEffectId(HapticMaterialId, 1, maxComputedLoops, vibrationOffset);
263 isPlaying = false;
264 float totalTimePlayed = maxComputedLoops * (float)hapticEffectDuration;
265 DebugMode($"Finished playing haptics at {Time.time} after {totalTimePlayed} seconds");
266#else
267 if (playingCoroutine != null)
268 {
269 StopCoroutine(playingCoroutine);
270 }
271 playingCoroutine = StartCoroutine(ControlVibration());
272#endif
273 }
274
279 public virtual IEnumerator ControlVibration()
280 {
281 DebugMode(string.Format("Started playing haptics! + {0}", Time.time));
282
283 // Coroutine logic for other platforms
284 if (isLooping)
285 {
287 }
288 Play();
289 float effectStartTime = Time.time;
290 float effectEndTime = effectStartTime + maxLoopTime;
291 // Check if we have a valid maxLoopTime, otherwise keep playing indefinitely
292 if (maxLoopTime > 0)
293 {
294 // Loop until the maxLoopTime is reached
295 while (Time.time < effectEndTime)
296 {
297 yield return null; // Wait until the next frame
298 }
299 // Once the maxLoopTime is exceeded, stop the effect
300 DebugMode($"Stopped playing haptics at {Time.time} after reaching max loop time of {maxLoopTime} seconds");
301 if (isLooping)
302 {
303 ApplyLooping(0);
304 }
305 isPlaying = false;
306 }
307 else
308 {
309 // If maxLoopTime is not valid (e.g., -1 for indefinite), just wait for the duration of the effect
310 yield return new WaitForSeconds((float)hapticEffectDuration);
311 }
312 // Clean up
313 playingCoroutine = null;
314 }
315 }
316}
Represents a haptic material, which is a ScriptableObject in Unity. This class is used to handle hapt...
virtual void Update()
Update the haptic effect settings at every frame.
virtual void Start()
Initialize the haptic effect settings at the start of the game.
virtual void PlayEventVibration()
Method to start the coroutine from outside (if necessary). Plays the haptic effect after the vibratio...
virtual void Play()
Call this method to play the haptic effect.
virtual IEnumerator ControlVibration()
Controls the vibration perception based on the full length of the haptic material; stops any residual...
void RemoveTarget(List< HapticBodyMapping.CommandData > Target)
Call this method to remove a target from the haptic effect.
virtual void Awake()
Add the haptic effect file to the when the object is created. The haptic effect file can be in the St...
void DebugMode(string debugMessage)
Debug method to print messages in the console only when debugMode is enabled.
virtual void Stop()
Call this method to stop the haptic effect.
void AddTarget(List< HapticBodyMapping.CommandData > Target)
Call this method to add a target to the haptic effect.
void ApplySourceIntensity()
Call this method to apply the source intensity.
void ApplyLooping(int loopValue)
Call this method to apply the looping state.