#if ENABLE_CLOUD_SERVICES_ANALYTICS || UNITY_2023_2_OR_NEWER using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit.Locomotion; using UnityEngine.XR.Interaction.Toolkit.Locomotion.Comfort; namespace UnityEditor.XR.Interaction.Toolkit.Analytics.Hooks { /// /// Tracks the activation status of various locomotion providers during a play mode session. /// class LocomotionProviderTracker { /// /// The list of Locomotion Provider types that were present in the play mode session, /// allowing duplicates. /// readonly List m_ProviderTypes = new List(); /// /// The set of Locomotion Provider types that were used, meaning they moved into the state. /// readonly HashSet m_ProviderTypesUsed = new HashSet(); /// /// Whether the comfort vignette was used. /// bool m_LocomotionComfortWasUsed; /// /// Resets 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 Reset() { m_ProviderTypes.Clear(); m_ProviderTypesUsed.Clear(); m_LocomotionComfortWasUsed = false; } /// /// Retrieves the current locomotion play mode data. /// /// Returns a containing the tracked locomotion data. NameCountUsageData GetLocomotionData() { var builtInTypesUnsorted = new List(); var unityTypesUnsorted = new List(); var customTypesUnsorted = new List(); var customDerivedComponents = new List(); foreach (var group in m_ProviderTypes.GroupBy(i => i)) { var typeCategory = XRIAnalyticsUtility.DetermineTypeCategory(group.Key); var count = group.Count(); var wasUsed = m_ProviderTypesUsed.Contains(group.Key); switch (typeCategory) { case XRIAnalyticsUtility.TypeCategory.BuiltIn: builtInTypesUnsorted.Add(new NameCountUsageData.NameCountUsageEntry { typeName = group.Key.Name, count = count, wasUsed = wasUsed, }); break; case XRIAnalyticsUtility.TypeCategory.Unity: unityTypesUnsorted.Add(new NameCountUsageData.NameCountUsageEntry { typeName = group.Key.FullName, count = count, wasUsed = wasUsed, }); break; case XRIAnalyticsUtility.TypeCategory.Custom: customDerivedComponents.AddRange(group); break; } } if (customDerivedComponents.Count > 0) { // Find the Unity type these custom components are derived from. foreach (var group in customDerivedComponents.GroupBy(XRIAnalyticsUtility.GetClosestUnityType)) { var count = group.Count(); var wasUsed = group.Any(i => m_ProviderTypesUsed.Contains(i)); var typeName = XRIAnalyticsUtility.IsXRIRuntimeAssembly(group.Key) ? group.Key.Name : group.Key.FullName; customTypesUnsorted.Add(new NameCountUsageData.NameCountUsageEntry { typeName = typeName, count = count, wasUsed = wasUsed, }); } } return new NameCountUsageData { builtInTypes = builtInTypesUnsorted.OrderByDescending(data => data.wasUsed).ThenBy(data => data.typeName).ToArray(), unityTypes = unityTypesUnsorted.OrderByDescending(data => data.wasUsed).ThenBy(data => data.typeName).ToArray(), customTypes = customTypesUnsorted.OrderByDescending(data => data.wasUsed).ThenBy(data => data.typeName).ToArray(), }; } /// /// 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.locomotionPlayModeData = GetLocomotionData(); payload.locomotionComfortWasUsed = m_LocomotionComfortWasUsed; } /// /// Start tracking the locomotion provider components. /// public void StartSession() { foreach (var provider in LocomotionProvider.locomotionProviders) { if (provider != null) { m_ProviderTypes.Add(provider.GetType()); provider.locomotionStarted += OnLocomotionStarted; } } LocomotionProvider.locomotionProvidersChanged += OnLocomotionProvidersChanged; TunnelingVignetteController.vignetteProviderQueued += OnVignetteProviderQueued; } /// /// End tracking the locomotion provider components. /// public void EndSession() { foreach (var provider in LocomotionProvider.locomotionProviders) { if (provider != null) provider.locomotionStarted -= OnLocomotionStarted; } LocomotionProvider.locomotionProvidersChanged -= OnLocomotionProvidersChanged; TunnelingVignetteController.vignetteProviderQueued -= OnVignetteProviderQueued; } void OnLocomotionProvidersChanged(LocomotionProvider provider) { // Handle a locomotion provider component being added after the start session. var providerType = provider.GetType(); m_ProviderTypes.Add(providerType); // Subscribe to the event to track when used (if necessary) if (!m_ProviderTypesUsed.Contains(providerType)) provider.locomotionStarted += OnLocomotionStarted; } void OnLocomotionStarted(LocomotionProvider provider) { // Once the locomotion provider instance has started once, // we no longer need to subscribe to the event since we only need to record // the fact that the type has been used. m_ProviderTypesUsed.Add(provider.GetType()); provider.locomotionStarted -= OnLocomotionStarted; } void OnVignetteProviderQueued(ITunnelingVignetteProvider provider) { m_LocomotionComfortWasUsed = true; TunnelingVignetteController.vignetteProviderQueued -= OnVignetteProviderQueued; } } } #endif