7#if UNITY_2019_3_OR_NEWER
8using UnityEngine.LowLevel;
9using UnityEngine.PlayerLoop;
11using UnityEngine.Experimental.LowLevel;
12using UnityEngine.Experimental.PlayerLoop;
23 internal struct InterhapticsPlayerLoop
26 internal struct UpdateHaptic { }
29 public static class HapticManager
32 private const float FIXED_TIMEFRAME = 0.03f;
36 public static bool DebugSwitch {
get;
set; } =
false;
40 public static bool MonoScriptingBackend {
get;
internal set; } =
false;
44 public static bool StopHapticsOnFocusLoss {
get;
set; } =
true;
45 private static float lastCall = 0;
47 [RuntimeInitializeOnLoadMethod]
48 private static void AppStart()
50 GeneratePlayerLoopNodes();
51 Application.quitting += OnApplicationQuit;
52 Application.focusChanged += OnFocusChanged;
54 UnityEditor.EditorApplication.pauseStateChanged += OnPauseStateChanged;
58 UnityEngine.Debug.LogWarning(
"Unity Editor in macOS will have dummy implementation of the Interhaptics Engine. To correctly debug in Unity use a Windows version of the Unity Editor.");
63 Internal.HapticDeviceManager.DeviceInitLoop();
64#if UNITY_ANDROID && !ENABLE_METAQUEST && !ENABLE_OPENXR && !UNITY_EDITOR
65 Platforms.Mobile.GenericAndroidHapticAbstraction.Initialize();
72 private static void GeneratePlayerLoopNodes()
74#if UNITY_2019_3_OR_NEWER
75 PlayerLoopSystem system = PlayerLoop.GetCurrentPlayerLoop();
77 PlayerLoopSystem system = PlayerLoop.GetDefaultPlayerLoop();
79 PlayerLoopSystem updateHaptic =
new PlayerLoopSystem()
81 updateDelegate = UpdateHaptic,
82 type = typeof(InterhapticsPlayerLoop.UpdateHaptic)
85 if (!InsertPlayerLoopNodeAfter<PostLateUpdate.UpdateAudio>(ref system, updateHaptic) &&
86 !InsertPlayerLoopNodeAfter<PostLateUpdate>(ref system, updateHaptic))
88 Debug.LogError(
"[Interhaptics] Impossible to initialize the haptic rendering loop");
92 PlayerLoop.SetPlayerLoop(system);
95 private static bool InsertPlayerLoopNodeAfter<T>(ref PlayerLoopSystem system, PlayerLoopSystem elementToInsert)
97 if (system.subSystemList ==
null)
103 for (var i = 0; i < system.subSystemList.Length; i++)
105 if (system.subSystemList[i].type == typeof(T))
108 System.Array.Resize(ref system.subSystemList, system.subSystemList.Length + 1);
109 System.Array.Copy(system.subSystemList, i, system.subSystemList, i + 1, system.subSystemList.Length - i - 1);
110 system.subSystemList[i] = elementToInsert;
115 if (InsertPlayerLoopNodeAfter<T>(ref system.subSystemList[i], elementToInsert))
125 private static void UpdateHaptic()
127 if (!Application.isPlaying)
131 if (Time.realtimeSinceStartup - lastCall < FIXED_TIMEFRAME)
137#if !UNITY_IOS && (!UNITY_ANDROID || ENABLE_METAQUEST || ENABLE_OPENXR || UNITY_EDITOR)
138 Core.HAR.ComputeAllEvents(Time.realtimeSinceStartup);
141 Internal.HapticDeviceManager.DeviceRenderLoop();
142 lastCall = Time.realtimeSinceStartup;
145 private static void OnFocusChanged(
bool hasFocus)
148 if ((!hasFocus) && (StopHapticsOnFocusLoss))
150 Core.HAR.ClearActiveEvents();
155 private static void OnPauseStateChanged(UnityEditor.PauseState state)
158 if ((UnityEditor.PauseState.Paused == state) && (StopHapticsOnFocusLoss))
160 Core.HAR.ClearActiveEvents();
165 private static void OnApplicationQuit()
168 Internal.HapticDeviceManager.DeviceCleanLoop();
169 Core.HAR.ClearActiveEvents();
170 Core.HAR.ClearInactiveEvents();
175 UnityEditor.EditorApplication.pauseStateChanged -= OnPauseStateChanged;
177 Application.focusChanged -= OnFocusChanged;
178 Application.quitting -= OnApplicationQuit;