using System; using UnityEngine.Assertions; using UnityEngine.XR.Interaction.Toolkit.Inputs; using UnityEngine.XR.Interaction.Toolkit.Locomotion; namespace UnityEngine.XR.Interaction.Toolkit { /// /// A locomotion provider that allows the user to rotate their rig using a 2D axis input. /// [Obsolete("SnapTurnProviderBase has been deprecated in XRI 3.0.0 and will be removed in a future version of XRI. Please use SnapTurnProvider instead.", false)] public abstract class SnapTurnProviderBase : LocomotionProvider { [SerializeField] [Tooltip("The number of degrees clockwise to rotate when snap turning clockwise.")] float m_TurnAmount = 45f; /// /// The number of degrees clockwise Unity rotates the rig when snap turning clockwise. /// public float turnAmount { get => m_TurnAmount; set => m_TurnAmount = value; } [SerializeField] [Tooltip("The amount of time that the system will wait before starting another snap turn.")] float m_DebounceTime = 0.5f; /// /// The amount of time that Unity waits before starting another snap turn. /// public float debounceTime { get => m_DebounceTime; set => m_DebounceTime = value; } [SerializeField] [Tooltip("Controls whether to enable left & right snap turns.")] bool m_EnableTurnLeftRight = true; /// /// Controls whether to enable left and right snap turns. /// /// public bool enableTurnLeftRight { get => m_EnableTurnLeftRight; set => m_EnableTurnLeftRight = value; } [SerializeField] [Tooltip("Controls whether to enable 180° snap turns.")] bool m_EnableTurnAround = true; /// /// Controls whether to enable 180° snap turns. /// /// public bool enableTurnAround { get => m_EnableTurnAround; set => m_EnableTurnAround = value; } [SerializeField] [Tooltip("The time (in seconds) to delay the first turn after receiving initial input for the turn.")] float m_DelayTime; /// /// The time (in seconds) to delay the first turn after receiving initial input for the turn. /// Subsequent turns while holding down input are delayed by the , not the delay time. /// This delay can be used, for example, as time to set a tunneling vignette effect as a VR comfort option. /// public float delayTime { get => m_DelayTime; set => m_DelayTime = value; } float m_CurrentTurnAmount; float m_TimeStarted; float m_DelayStartTime; bool m_TurnAroundActivated; /// protected override void Awake() { base.Awake(); if (system != null && m_DelayTime > 0f && m_DelayTime > system.timeout) Debug.LogWarning($"Delay Time ({m_DelayTime}) is longer than the Locomotion System's Timeout ({system.timeout}).", this); } /// /// See . /// protected void Update() { // Wait for a certain amount of time before allowing another turn. if (m_TimeStarted > 0f && (m_TimeStarted + m_DebounceTime < Time.time)) { m_TimeStarted = 0f; return; } // Reset to Idle state at the beginning of the update loop (rather than the end) // so that anything that needs to be aware of the Done state can trigger, such as // the vignette provider or another comfort mode option. if (locomotionPhase == LocomotionPhase.Done) locomotionPhase = LocomotionPhase.Idle; var input = ReadInput(); var amount = GetTurnAmount(input); if (Mathf.Abs(amount) > 0f || locomotionPhase == LocomotionPhase.Started) { StartTurn(amount); } else if (Mathf.Approximately(m_CurrentTurnAmount, 0f) && locomotionPhase == LocomotionPhase.Moving) { locomotionPhase = LocomotionPhase.Done; } if (locomotionPhase == LocomotionPhase.Moving && Math.Abs(m_CurrentTurnAmount) > 0f && BeginLocomotion()) { var xrOrigin = system.xrOrigin; if (xrOrigin != null) { xrOrigin.RotateAroundCameraUsingOriginUp(m_CurrentTurnAmount); } else { locomotionPhase = LocomotionPhase.Done; } m_CurrentTurnAmount = 0f; EndLocomotion(); if (Mathf.Approximately(amount, 0f)) locomotionPhase = LocomotionPhase.Done; } if (input == Vector2.zero) m_TurnAroundActivated = false; } /// /// Reads the current value of the snap turn input. /// /// Returns the input vector, such as from a thumbstick. protected abstract Vector2 ReadInput(); /// /// Determines the turn amount in degrees for the given vector. /// /// Input vector, such as from a thumbstick. /// Returns the turn amount in degrees for the given vector. protected virtual float GetTurnAmount(Vector2 input) { if (input == Vector2.zero) return 0f; var cardinal = CardinalUtility.GetNearestCardinal(input); switch (cardinal) { case Cardinal.North: break; case Cardinal.South: if (m_EnableTurnAround && !m_TurnAroundActivated) return 180f; break; case Cardinal.East: if (m_EnableTurnLeftRight) return m_TurnAmount; break; case Cardinal.West: if (m_EnableTurnLeftRight) return -m_TurnAmount; break; default: Assert.IsTrue(false, $"Unhandled {nameof(Cardinal)}={cardinal}"); break; } return 0f; } /// /// Begins turning locomotion. /// /// Amount to turn. protected void StartTurn(float amount) { if (m_TimeStarted > 0f) return; if (!CanBeginLocomotion()) return; if (Mathf.Approximately(amount, 180f)) m_TurnAroundActivated = true; if (locomotionPhase == LocomotionPhase.Idle) { locomotionPhase = LocomotionPhase.Started; m_DelayStartTime = Time.time; } // We set the m_CurrentTurnAmount here so we can still trigger the turn // in the case where the input is released before the delay timeout happens. if (Math.Abs(amount) > 0f) m_CurrentTurnAmount = amount; // Wait for configured Delay Time if (m_DelayTime > 0f && Time.time - m_DelayStartTime < m_DelayTime) return; locomotionPhase = LocomotionPhase.Moving; m_TimeStarted = Time.time; } } }