using System.Collections.Generic; using Unity.XR.CoreUtils; namespace UnityEngine.XR.Interaction.Toolkit.Locomotion { /// /// Behavior that mediates user locomotion by providing components with access to the /// linked to this behavior. This behavior manages the /// for each provider based on its requests for the Body Transformer. /// [AddComponentMenu("XR/Locomotion/Locomotion Mediator", 11)] [HelpURL(XRHelpURLConstants.k_LocomotionMediator)] [RequireComponent(typeof(XRBodyTransformer))] public class LocomotionMediator : MonoBehaviour { class LocomotionProviderData { public LocomotionState state; public int locomotionEndFrame; } /// /// The XR Origin controlled by the for locomotion. /// public XROrigin xrOrigin { get => m_XRBodyTransformer.xrOrigin; set => m_XRBodyTransformer.xrOrigin = value; } XRBodyTransformer m_XRBodyTransformer; readonly Dictionary m_ProviderDataMap = new Dictionary(); static readonly List s_ProvidersToRemove = new List(); /// /// See . /// protected void Awake() { m_XRBodyTransformer = GetComponent(); } /// /// See . /// protected void Update() { s_ProvidersToRemove.Clear(); foreach (var kvp in m_ProviderDataMap) { var provider = kvp.Key; if (provider == null) { s_ProvidersToRemove.Add(provider); continue; } var providerData = kvp.Value; if (providerData.state == LocomotionState.Preparing && provider.canStartMoving) { StartLocomotion(provider, providerData); } else if (providerData.state == LocomotionState.Ended && Time.frameCount > providerData.locomotionEndFrame) { providerData.state = LocomotionState.Idle; } } foreach (var provider in s_ProvidersToRemove) { m_ProviderDataMap.Remove(provider); } } internal bool TryPrepareLocomotion(LocomotionProvider provider) { if (!m_ProviderDataMap.TryGetValue(provider, out var providerData)) { // We can skip checking state because it is assumed to be Idle if it is not in the map. providerData = new LocomotionProviderData(); m_ProviderDataMap[provider] = providerData; } else if (GetProviderLocomotionState(provider).IsActive()) { return false; } providerData.state = LocomotionState.Preparing; return true; } internal bool TryStartLocomotion(LocomotionProvider provider) { if (!m_ProviderDataMap.TryGetValue(provider, out var providerData)) { // We can skip checking state because it is assumed to be Idle if it is not in the map. providerData = new LocomotionProviderData(); m_ProviderDataMap[provider] = providerData; } else if (GetProviderLocomotionState(provider) == LocomotionState.Moving) { return false; } StartLocomotion(provider, providerData); return true; } void StartLocomotion(LocomotionProvider provider, LocomotionProviderData providerData) { providerData.state = LocomotionState.Moving; provider.OnLocomotionStart(m_XRBodyTransformer); } internal bool TryEndLocomotion(LocomotionProvider provider) { if (!m_ProviderDataMap.TryGetValue(provider, out var providerData)) return false; var locomotionState = providerData.state; if (!locomotionState.IsActive()) return false; providerData.state = LocomotionState.Ended; providerData.locomotionEndFrame = Time.frameCount; provider.OnLocomotionEnd(); return true; } /// /// Queries the state of locomotion for the given provider. /// /// The provider whose locomotion state to query. /// Returns the state of locomotion for the given . This returns /// if the provider is not managed by this mediator. public LocomotionState GetProviderLocomotionState(LocomotionProvider provider) { return m_ProviderDataMap.TryGetValue(provider, out var providerData) ? providerData.state : LocomotionState.Idle; } } }