DynamicMoveProvider.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using Unity.XR.CoreUtils;
  2. using UnityEngine.Assertions;
  3. using UnityEngine.XR.Interaction.Toolkit.Locomotion.Movement;
  4. namespace UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets
  5. {
  6. /// <summary>
  7. /// A version of continuous movement that automatically controls the frame of reference that
  8. /// determines the forward direction of movement based on user preference for each hand.
  9. /// For example, can configure to use head relative movement for the left hand and controller relative movement for the right hand.
  10. /// </summary>
  11. public class DynamicMoveProvider : ContinuousMoveProvider
  12. {
  13. /// <summary>
  14. /// Defines which transform the XR Origin's movement direction is relative to.
  15. /// </summary>
  16. /// <seealso cref="leftHandMovementDirection"/>
  17. /// <seealso cref="rightHandMovementDirection"/>
  18. public enum MovementDirection
  19. {
  20. /// <summary>
  21. /// Use the forward direction of the head (camera) as the forward direction of the XR Origin's movement.
  22. /// </summary>
  23. HeadRelative,
  24. /// <summary>
  25. /// Use the forward direction of the hand (controller) as the forward direction of the XR Origin's movement.
  26. /// </summary>
  27. HandRelative,
  28. }
  29. [Space, Header("Movement Direction")]
  30. [SerializeField]
  31. [Tooltip("Directs the XR Origin's movement when using the head-relative mode. If not set, will automatically find and use the XR Origin Camera.")]
  32. Transform m_HeadTransform;
  33. /// <summary>
  34. /// Directs the XR Origin's movement when using the head-relative mode. If not set, will automatically find and use the XR Origin Camera.
  35. /// </summary>
  36. public Transform headTransform
  37. {
  38. get => m_HeadTransform;
  39. set => m_HeadTransform = value;
  40. }
  41. [SerializeField]
  42. [Tooltip("Directs the XR Origin's movement when using the hand-relative mode with the left hand.")]
  43. Transform m_LeftControllerTransform;
  44. /// <summary>
  45. /// Directs the XR Origin's movement when using the hand-relative mode with the left hand.
  46. /// </summary>
  47. public Transform leftControllerTransform
  48. {
  49. get => m_LeftControllerTransform;
  50. set => m_LeftControllerTransform = value;
  51. }
  52. [SerializeField]
  53. [Tooltip("Directs the XR Origin's movement when using the hand-relative mode with the right hand.")]
  54. Transform m_RightControllerTransform;
  55. public Transform rightControllerTransform
  56. {
  57. get => m_RightControllerTransform;
  58. set => m_RightControllerTransform = value;
  59. }
  60. [SerializeField]
  61. [Tooltip("Whether to use the specified head transform or left controller transform to direct the XR Origin's movement for the left hand.")]
  62. MovementDirection m_LeftHandMovementDirection;
  63. /// <summary>
  64. /// Whether to use the specified head transform or controller transform to direct the XR Origin's movement for the left hand.
  65. /// </summary>
  66. /// <seealso cref="MovementDirection"/>
  67. public MovementDirection leftHandMovementDirection
  68. {
  69. get => m_LeftHandMovementDirection;
  70. set => m_LeftHandMovementDirection = value;
  71. }
  72. [SerializeField]
  73. [Tooltip("Whether to use the specified head transform or right controller transform to direct the XR Origin's movement for the right hand.")]
  74. MovementDirection m_RightHandMovementDirection;
  75. /// <summary>
  76. /// Whether to use the specified head transform or controller transform to direct the XR Origin's movement for the right hand.
  77. /// </summary>
  78. /// <seealso cref="MovementDirection"/>
  79. public MovementDirection rightHandMovementDirection
  80. {
  81. get => m_RightHandMovementDirection;
  82. set => m_RightHandMovementDirection = value;
  83. }
  84. Transform m_CombinedTransform;
  85. Pose m_LeftMovementPose = Pose.identity;
  86. Pose m_RightMovementPose = Pose.identity;
  87. /// <inheritdoc />
  88. protected override void Awake()
  89. {
  90. base.Awake();
  91. m_CombinedTransform = new GameObject("[Dynamic Move Provider] Combined Forward Source").transform;
  92. m_CombinedTransform.SetParent(transform, false);
  93. m_CombinedTransform.localPosition = Vector3.zero;
  94. m_CombinedTransform.localRotation = Quaternion.identity;
  95. forwardSource = m_CombinedTransform;
  96. }
  97. /// <inheritdoc />
  98. protected override Vector3 ComputeDesiredMove(Vector2 input)
  99. {
  100. // Don't need to do anything if the total input is zero.
  101. // This is the same check as the base method.
  102. if (input == Vector2.zero)
  103. return base.ComputeDesiredMove(input);
  104. // Initialize the Head Transform if necessary, getting the Camera from XR Origin
  105. if (m_HeadTransform == null)
  106. {
  107. var xrOrigin = mediator.xrOrigin;
  108. if (xrOrigin != null)
  109. {
  110. var xrCamera = xrOrigin.Camera;
  111. if (xrCamera != null)
  112. m_HeadTransform = xrCamera.transform;
  113. }
  114. }
  115. // Get the forward source for the left hand input
  116. switch (m_LeftHandMovementDirection)
  117. {
  118. case MovementDirection.HeadRelative:
  119. if (m_HeadTransform != null)
  120. m_LeftMovementPose = m_HeadTransform.GetWorldPose();
  121. break;
  122. case MovementDirection.HandRelative:
  123. if (m_LeftControllerTransform != null)
  124. m_LeftMovementPose = m_LeftControllerTransform.GetWorldPose();
  125. break;
  126. default:
  127. Assert.IsTrue(false, $"Unhandled {nameof(MovementDirection)}={m_LeftHandMovementDirection}");
  128. break;
  129. }
  130. // Get the forward source for the right hand input
  131. switch (m_RightHandMovementDirection)
  132. {
  133. case MovementDirection.HeadRelative:
  134. if (m_HeadTransform != null)
  135. m_RightMovementPose = m_HeadTransform.GetWorldPose();
  136. break;
  137. case MovementDirection.HandRelative:
  138. if (m_RightControllerTransform != null)
  139. m_RightMovementPose = m_RightControllerTransform.GetWorldPose();
  140. break;
  141. default:
  142. Assert.IsTrue(false, $"Unhandled {nameof(MovementDirection)}={m_RightHandMovementDirection}");
  143. break;
  144. }
  145. // Combine the two poses into the forward source based on the magnitude of input
  146. var leftHandValue = leftHandMoveInput.ReadValue();
  147. var rightHandValue = rightHandMoveInput.ReadValue();
  148. var totalSqrMagnitude = leftHandValue.sqrMagnitude + rightHandValue.sqrMagnitude;
  149. var leftHandBlend = 0.5f;
  150. if (totalSqrMagnitude > Mathf.Epsilon)
  151. leftHandBlend = leftHandValue.sqrMagnitude / totalSqrMagnitude;
  152. var combinedPosition = Vector3.Lerp(m_RightMovementPose.position, m_LeftMovementPose.position, leftHandBlend);
  153. var combinedRotation = Quaternion.Slerp(m_RightMovementPose.rotation, m_LeftMovementPose.rotation, leftHandBlend);
  154. m_CombinedTransform.SetPositionAndRotation(combinedPosition, combinedRotation);
  155. return base.ComputeDesiredMove(input);
  156. }
  157. }
  158. }