using System; using UnityEngine.Assertions; using UnityEngine.XR.Interaction.Toolkit.Inputs; using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; using UnityEngine.XR.Interaction.Toolkit.Locomotion.Movement; namespace UnityEngine.XR.Interaction.Toolkit.Locomotion.Turning { /// /// Locomotion provider that allows the user to smoothly rotate their rig continuously over time /// based on read input values, such as from the controller thumbstick. /// /// /// /// [AddComponentMenu("XR/Locomotion/Continuous Turn Provider", 11)] [HelpURL(XRHelpURLConstants.k_ContinuousTurnProvider)] public class ContinuousTurnProvider : LocomotionProvider { [SerializeField] [Tooltip("The number of degrees/second clockwise to rotate when turning clockwise.")] float m_TurnSpeed = 60f; /// /// The number of degrees/second clockwise to rotate when turning clockwise. /// public float turnSpeed { get => m_TurnSpeed; set => m_TurnSpeed = value; } [SerializeField] [Tooltip("Controls whether to enable left & right continuous turns.")] bool m_EnableTurnLeftRight = true; /// /// Controls whether to enable left and right continuous turns. /// public bool enableTurnLeftRight { get => m_EnableTurnLeftRight; set => m_EnableTurnLeftRight = value; } [SerializeField] [Tooltip("Controls whether to enable 180° snap turns on the South direction.")] bool m_EnableTurnAround; /// /// Controls whether to enable 180° snap turns on the South direction. /// public bool enableTurnAround { get => m_EnableTurnAround; set => m_EnableTurnAround = value; } [SerializeField] [Tooltip("Reads input data from the left hand controller. Input Action must be a Value action type (Vector 2).")] XRInputValueReader m_LeftHandTurnInput = new XRInputValueReader("Left Hand Turn"); /// /// Reads input data from the left hand controller. Input Action must be a Value action type (Vector 2). /// public XRInputValueReader leftHandTurnInput { get => m_LeftHandTurnInput; set => XRInputReaderUtility.SetInputProperty(ref m_LeftHandTurnInput, value, this); } [SerializeField] [Tooltip("Reads input data from the right hand controller. Input Action must be a Value action type (Vector 2).")] XRInputValueReader m_RightHandTurnInput = new XRInputValueReader("Right Hand Turn"); /// /// Reads input data from the right hand controller. Input Action must be a Value action type (Vector 2). /// public XRInputValueReader rightHandTurnInput { get => m_RightHandTurnInput; set => XRInputReaderUtility.SetInputProperty(ref m_RightHandTurnInput, value, this); } /// /// The transformation that is used by this component to apply turn movement. /// public XRBodyYawRotation transformation { get; set; } = new XRBodyYawRotation(); bool m_IsTurningXROrigin; bool m_TurnAroundActivated; /// /// See . /// protected void OnEnable() { // Enable and disable directly serialized actions with this behavior's enabled lifecycle. m_LeftHandTurnInput.EnableDirectActionIfModeUsed(); m_RightHandTurnInput.EnableDirectActionIfModeUsed(); } /// /// See . /// protected void OnDisable() { m_LeftHandTurnInput.DisableDirectActionIfModeUsed(); m_RightHandTurnInput.DisableDirectActionIfModeUsed(); } /// /// See . /// protected void Update() { m_IsTurningXROrigin = false; // Use the input amount to scale the turn speed. var input = ReadInput(); var turnAmount = GetTurnAmount(input); TurnRig(turnAmount); if (!m_IsTurningXROrigin) TryEndLocomotion(); if (input == Vector2.zero) m_TurnAroundActivated = false; } Vector2 ReadInput() { var leftHandValue = m_LeftHandTurnInput.ReadValue(); var rightHandValue = m_RightHandTurnInput.ReadValue(); return leftHandValue + rightHandValue; } /// /// 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) { 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: case Cardinal.West: if (m_EnableTurnLeftRight) return input.magnitude * (Mathf.Sign(input.x) * m_TurnSpeed * Time.deltaTime); break; default: Assert.IsTrue(false, $"Unhandled {nameof(Cardinal)}={cardinal}"); break; } return 0f; } /// /// Rotates the rig by degrees. /// /// The amount of rotation in degrees. protected void TurnRig(float turnAmount) { if (Mathf.Approximately(turnAmount, 0f)) return; if (Mathf.Approximately(turnAmount, 180f)) m_TurnAroundActivated = true; TryStartLocomotionImmediately(); if (locomotionState != LocomotionState.Moving) return; m_IsTurningXROrigin = true; transformation.angleDelta = turnAmount; TryQueueTransformation(transformation); } } }