using UnityEngine.Assertions; using UnityEngine.Scripting.APIUpdating; using UnityEngine.XR.Interaction.Toolkit.Locomotion.Gravity; using UnityEngine.XR.Interaction.Toolkit.Utilities; namespace UnityEngine.XR.Interaction.Toolkit.Locomotion.Movement { /// /// Base class for a locomotion provider that allows for constrained movement with a . /// /// [MovedFrom("UnityEngine.XR.Interaction.Toolkit")] public abstract partial class ConstrainedMoveProvider : LocomotionProvider { [SerializeField] [Tooltip("Controls whether to enable unconstrained movement along the x-axis.")] bool m_EnableFreeXMovement = true; /// /// Controls whether to enable unconstrained movement along the x-axis. /// public bool enableFreeXMovement { get => m_EnableFreeXMovement; set => m_EnableFreeXMovement = value; } [SerializeField] [Tooltip("Controls whether to enable unconstrained movement along the y-axis.")] bool m_EnableFreeYMovement; /// /// Controls whether to enable unconstrained movement along the y-axis. /// public bool enableFreeYMovement { get => m_EnableFreeYMovement; set => m_EnableFreeYMovement = value; } [SerializeField] [Tooltip("Controls whether to enable unconstrained movement along the z-axis.")] bool m_EnableFreeZMovement = true; /// /// Controls whether to enable unconstrained movement along the z-axis. /// public bool enableFreeZMovement { get => m_EnableFreeZMovement; set => m_EnableFreeZMovement = value; } /// /// The transformation that is used by this component to apply translation movement. /// public XROriginMovement transformation { get; set; } = new XROriginMovement(); CharacterController m_CharacterController; bool m_AttemptedGetCharacterController; bool m_IsMovingXROrigin; GravityProvider m_GravityProvider; /// protected override void Awake() { base.Awake(); if (ComponentLocatorUtility.TryFindComponent(out m_GravityProvider)) { #pragma warning disable CS0618 // Type or member is obsolete -- Assist with migration when this component wants gravity disabled if (!m_UseGravity) MigrateUseGravityToGravityProvider(); #pragma warning restore CS0618 } } /// /// See . /// protected void Update() { m_IsMovingXROrigin = false; var xrOrigin = mediator.xrOrigin?.Origin; if (xrOrigin == null) return; var translationInWorldSpace = ComputeDesiredMove(out var attemptingMove); #pragma warning disable CS0618 // Type or member is obsolete switch (m_GravityApplicationMode) { case GravityApplicationMode.Immediately: MoveRig(translationInWorldSpace); break; case GravityApplicationMode.AttemptingMove: if (attemptingMove || m_GravityDrivenVelocity != Vector3.zero) MoveRig(translationInWorldSpace); break; default: Assert.IsTrue(false, $"{nameof(m_GravityApplicationMode)}={m_GravityApplicationMode} outside expected range."); break; } #pragma warning restore CS0618 // Type or member is obsolete if (!m_IsMovingXROrigin) TryEndLocomotion(); } /// /// Determines how much to move the rig. /// /// Whether the provider is attempting to move. /// Returns the translation amount in world space to move the rig. protected abstract Vector3 ComputeDesiredMove(out bool attemptingMove); /// /// Creates a locomotion event to move the rig by , /// and optionally restricts movement along each axis and applies gravity. /// /// The translation amount in world space to move the rig /// (before restricting movement along each axis and applying gravity). protected virtual void MoveRig(Vector3 translationInWorldSpace) { FindCharacterController(); var motion = translationInWorldSpace; if (!m_EnableFreeXMovement) motion.x = 0f; if (!m_EnableFreeYMovement) motion.y = 0f; if (!m_EnableFreeZMovement) motion.z = 0f; // Step vertical velocity from gravity, but only if gravity is not being controlled by Gravity Provider if (m_GravityProvider == null && m_CharacterController != null && m_CharacterController.enabled) { #pragma warning disable CS0618 // Type or member is obsolete if (!m_UseGravity || m_CharacterController.isGrounded) { m_GravityDrivenVelocity = Vector3.zero; } else { m_GravityDrivenVelocity += Physics.gravity * Time.deltaTime; if (m_EnableFreeXMovement) m_GravityDrivenVelocity.x = 0f; if (m_EnableFreeYMovement) m_GravityDrivenVelocity.y = 0f; if (m_EnableFreeZMovement) m_GravityDrivenVelocity.z = 0f; } motion += m_GravityDrivenVelocity * Time.deltaTime; #pragma warning restore CS0618 // Type or member is obsolete } TryStartLocomotionImmediately(); if (locomotionState != LocomotionState.Moving) return; // Note that calling Move even with Vector3.zero will have an effect by causing isGrounded to update m_IsMovingXROrigin = true; transformation.motion = motion; TryQueueTransformation(transformation); } void FindCharacterController() { var xrOrigin = mediator.xrOrigin?.Origin; if (xrOrigin == null) return; // Save a reference to the optional CharacterController on the rig GameObject // that will be used to move instead of modifying the Transform directly. if (m_CharacterController == null && !m_AttemptedGetCharacterController) { // Try on the Origin GameObject first, and then fallback to the XR Origin GameObject (if different) if (!xrOrigin.TryGetComponent(out m_CharacterController) && xrOrigin != mediator.xrOrigin.gameObject) mediator.xrOrigin.TryGetComponent(out m_CharacterController); m_AttemptedGetCharacterController = true; } } } }