#if ENABLE_CLOUD_SERVICES_ANALYTICS || UNITY_2023_2_OR_NEWER using System.Collections.Generic; using UnityEngine.Pool; using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactables; using UnityEngine.XR.Interaction.Toolkit.Interactors; namespace UnityEditor.XR.Interaction.Toolkit.Analytics.Hooks { /// /// Runtime tracker for XR Interaction Manager registration events. /// This class is used to track the number of enabled interaction managers along with /// the number of registered interactors and interactables during a play mode session. /// /// /// class InteractionManagerTracker { /// /// The maximum number of interactor or interactable instances to track in a . /// This keeps the memory usage of the tracker bounded. /// /// /// The peak count is allowed to report above this number since we are just tracking the int count, /// so no need to limit that since the purpose of this limit is to keep memory usage bounded. /// const int k_MaxHashSetCount = 500; /// /// Duration in seconds of at least one enabled interaction manager during a play mode session. /// /// /// This duration can range from 0 seconds to the total length of the play mode session, /// and does not differ based on multiple managers being enabled simultaneously. /// public double sessionDuration { get; private set; } /// /// Peak number of enabled interaction managers during a play mode session. /// public int interactionManagersPeakCount { get; private set; } /// /// Total count of different interaction managers that were enabled during a play mode session. /// public int interactionManagersObjectCount => m_SubscribedManagers.Count; /// /// Peak number of registered interactors across all interaction managers during a play mode session. /// public int interactorsPeakRegisteredCount { get; private set; } /// /// Total count of different interactors that were registered during a play mode session. /// public int interactorsObjectRegisteredCount => m_Interactors.Count; /// /// Peak number of registered interactables across all interaction managers during a play mode session. /// public int interactablesPeakRegisteredCount { get; private set; } /// /// Total count of different interactables that were registered during a play mode session. /// public int interactablesObjectRegisteredCount => m_Interactables.Count; List m_EnabledManagers = new List(); List m_SubscribedManagers = new List(); /// /// All interactors that were registered at any point during a play mode session. /// HashSet m_Interactors = new HashSet(); /// /// All interactables that were registered at any point during a play mode session. /// HashSet m_Interactables = new HashSet(); double m_StartTimestamp; int m_CurrentInteractorsRegisteredCount; int m_CurrentInteractablesRegisteredCount; /// /// Reset the tracker to its initial state. /// Call this after generating the analytics payload to avoid accumulating data across /// multiple play mode sessions when domain reload is disabled. /// public void Cleanup() { foreach (var manager in m_SubscribedManagers) { if (manager != null) { manager.interactorRegistered -= OnInteractorRegistered; manager.interactorUnregistered -= OnInteractorUnregistered; manager.interactableRegistered -= OnInteractableRegistered; manager.interactableUnregistered -= OnInteractableUnregistered; } } sessionDuration = 0d; interactionManagersPeakCount = 0; interactorsPeakRegisteredCount = 0; interactablesPeakRegisteredCount = 0; m_EnabledManagers.Clear(); m_SubscribedManagers.Clear(); m_Interactors.Clear(); m_Interactables.Clear(); m_StartTimestamp = 0d; m_CurrentInteractorsRegisteredCount = 0; m_CurrentInteractablesRegisteredCount = 0; } /// /// Start tracking the manager component, invoked when its OnEnable is called. /// /// The manager component to track. /// Current timestamp. public void StartSession(XRInteractionManager manager, double now) { if (m_EnabledManagers.Contains(manager)) return; m_EnabledManagers.Add(manager); if (m_EnabledManagers.Count > interactionManagersPeakCount) interactionManagersPeakCount = m_EnabledManagers.Count; if (!m_SubscribedManagers.Contains(manager)) { m_SubscribedManagers.Add(manager); // Developer reminder to update Cleanup() if new events are subscribed to here. manager.interactorRegistered += OnInteractorRegistered; manager.interactorUnregistered += OnInteractorUnregistered; manager.interactableRegistered += OnInteractableRegistered; manager.interactableUnregistered += OnInteractableUnregistered; // Add any existing interactors that are already registered before subscribing. using (ListPool.Get(out var scratchInteractors)) { manager.GetRegisteredInteractors(scratchInteractors); m_CurrentInteractorsRegisteredCount += scratchInteractors.Count; foreach (var interactor in scratchInteractors) { AddToHashSet(interactor); } } // Add any existing interactables that are already registered before subscribing. using (ListPool.Get(out var scratchInteractables)) { manager.GetRegisteredInteractables(scratchInteractables); m_CurrentInteractablesRegisteredCount += scratchInteractables.Count; foreach (var interactable in scratchInteractables) { AddToHashSet(interactable); } } UpdatePeakCount(); } if (m_EnabledManagers.Count == 1) m_StartTimestamp = now; } /// /// Start tracking the manager component, invoked when its OnDisable is called. /// /// The manager component to track. /// Current timestamp. public void EndSession(XRInteractionManager manager, double now) { if (m_EnabledManagers.Remove(manager) && m_EnabledManagers.Count == 0) sessionDuration += now - m_StartTimestamp; } /// /// Update the analytics payload struct with the data from this tracker. /// /// The analytics payload to write into. public void UpdateEventPayload(ref XRIPlayModeEvent.Payload payload) { payload.interactionManagersPeakCount = interactionManagersPeakCount; payload.interactionManagersObjectCount = interactionManagersObjectCount; payload.interactorsPeakRegisteredCount = interactorsPeakRegisteredCount; payload.interactorsObjectRegisteredCount = interactorsObjectRegisteredCount; payload.interactablesPeakRegisteredCount = interactablesPeakRegisteredCount; payload.interactablesObjectRegisteredCount = interactablesObjectRegisteredCount; payload.interactionManagerDurationSeconds = (float)sessionDuration; } void UpdatePeakCount() { if (m_CurrentInteractorsRegisteredCount > interactorsPeakRegisteredCount) interactorsPeakRegisteredCount = m_CurrentInteractorsRegisteredCount; if (m_CurrentInteractablesRegisteredCount > interactablesPeakRegisteredCount) interactablesPeakRegisteredCount = m_CurrentInteractablesRegisteredCount; } void AddToHashSet(IXRInteractor interactor) { if (m_Interactors.Count < k_MaxHashSetCount) m_Interactors.Add(interactor); } void AddToHashSet(IXRInteractable interactable) { if (m_Interactables.Count < k_MaxHashSetCount) m_Interactables.Add(interactable); } void OnInteractorRegistered(InteractorRegisteredEventArgs args) { AddToHashSet(args.interactorObject); m_CurrentInteractorsRegisteredCount++; UpdatePeakCount(); } void OnInteractorUnregistered(InteractorUnregisteredEventArgs args) { m_CurrentInteractorsRegisteredCount--; } void OnInteractableRegistered(InteractableRegisteredEventArgs args) { AddToHashSet(args.interactableObject); m_CurrentInteractablesRegisteredCount++; UpdatePeakCount(); } void OnInteractableUnregistered(InteractableUnregisteredEventArgs args) { m_CurrentInteractablesRegisteredCount--; } } } #endif