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; /// /// The linked with this mediator. /// public XRBodyTransformer bodyTransformer => 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) { ChangeState(provider, providerData, LocomotionState.Moving); } else if (providerData.state == LocomotionState.Ended && Time.frameCount > providerData.locomotionEndFrame) { ChangeState(provider, providerData, LocomotionState.Idle); } } if (s_ProvidersToRemove.Count > 0) { 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; } ChangeState(provider, providerData, 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; } ChangeState(provider, providerData, LocomotionState.Moving); return true; } internal bool TryEndLocomotion(LocomotionProvider provider) { if (!m_ProviderDataMap.TryGetValue(provider, out var providerData)) return false; var locomotionState = providerData.state; if (!locomotionState.IsActive()) return false; ChangeState(provider, providerData, LocomotionState.Ended); return true; } void ChangeState(LocomotionProvider provider, LocomotionProviderData providerData, LocomotionState state) { if (providerData.state == state) return; var oldState = providerData.state; providerData.state = state; if (state == LocomotionState.Ended) providerData.locomotionEndFrame = Time.frameCount; provider.OnLocomotionStateChanging(oldState, state, m_XRBodyTransformer); } /// /// 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; } } }