static IEnumerator WaitForSteadyState(XRBaseInteractable.MovementType movementType) { yield return(null); if (movementType == XRBaseInteractable.MovementType.VelocityTracking) { yield return(new WaitForFixedUpdate()); } yield return(new WaitForFixedUpdate()); }
/// <summary>This method is called by the interaction manager /// when the interactor ends selection of an interactable.</summary> /// <param name="interactor">Interactor that is ending the selection.</param> protected internal override void OnSelectExit(XRBaseInteractor interactor) { base.OnSelectExit(interactor); // reset RididBody settings m_RigidBody.isKinematic = m_WasKinematic; m_RigidBody.useGravity = m_UsedGravity | m_GravityOnDetach; m_CurrentMovementType = m_MovementType; m_SelectingInteractor = null; m_DetachInLateUpdate = true; SmoothVelocityEnd(); }
protected override void Awake() { base.Awake(); m_CurrentMovementType = m_MovementType; if (m_RigidBody == null) { m_RigidBody = GetComponent <Rigidbody>(); } if (m_RigidBody == null) { Debug.LogWarning("Grab Interactable does not have a required RigidBody.", this); } }
/// <summary>This method is called by the interaction manager /// when the interactor first initiates selection of an interactable.</summary> /// <param name="interactor">Interactor that is initiating the selection.</param> protected internal override void OnSelectEnter(XRBaseInteractor interactor) { if (!interactor) { return; } base.OnSelectEnter(interactor); // Only do this on the first select, because otherwise the original parent will be overwritten as null. if (m_SelectingInteractor == null) { m_OriginalSceneParent = transform.parent; transform.parent = null; } m_SelectingInteractor = interactor; // special case where the interactor will override this objects movement type (used for Sockets and other absolute interactors) m_CurrentMovementType = (interactor.selectedInteractableMovementTypeOverride != null) ? interactor.selectedInteractableMovementTypeOverride.Value : m_MovementType; // remember RigidBody settings and setup to move m_WasKinematic = m_RigidBody.isKinematic; m_UsedGravity = m_RigidBody.useGravity; m_RigidBody.isKinematic = (m_CurrentMovementType == XRBaseInteractable.MovementType.Kinematic); m_RigidBody.useGravity = false; m_RigidBody.drag = 0f; m_RigidBody.angularDrag = 0f; // forget if we have previous detach velocities m_DetachVelocity = m_DetachAngularVelocity = Vector3.zero; UpdateInteractorLocalPose(interactor); var teleportOnSelect = false; if (teleportOnSelect) { Teleport(m_SelectingInteractor.attachTransform); } else if (m_AttachEaseInTime > 0.0f) { m_TargetWorldPosition = m_RigidBody.worldCenterOfMass; m_TargetWorldRotation = transform.rotation; m_CurrentAttachEaseTime = 0f; } SmoothVelocityStart(); }
public IEnumerator CenteredObjectWithAttachTransformMovesToExpectedPosition([ValueSource(nameof(s_MovementTypes))] XRBaseInteractable.MovementType movementType) { // Create Grab Interactable at some arbitrary point var grabInteractableGO = GameObject.CreatePrimitive(PrimitiveType.Cube); grabInteractableGO.name = "Grab Interactable"; grabInteractableGO.transform.localPosition = new Vector3(1f, 2f, 3f); grabInteractableGO.transform.localRotation = Quaternion.identity; var boxCollider = grabInteractableGO.GetComponent <BoxCollider>(); var rigidbody = grabInteractableGO.AddComponent <Rigidbody>(); rigidbody.useGravity = false; rigidbody.isKinematic = true; var grabInteractable = grabInteractableGO.AddComponent <XRGrabInteractable>(); grabInteractable.movementType = movementType; DisableDelayProperties(grabInteractable); // Set the Attach Transform to the back upper-right corner of the cube // to test an attach transform different from the transform position (which is also its center). var grabInteractableAttach = new GameObject("Grab Interactable Attach").transform; var attachOffset = new Vector3(0.5f, 0.5f, -0.5f); grabInteractableAttach.SetParent(grabInteractable.transform); grabInteractableAttach.localPosition = attachOffset; grabInteractableAttach.localRotation = Quaternion.identity; grabInteractable.attachTransform = grabInteractableAttach; // The built-in Cube resource has its center at the center of the cube. var centerOffset = Vector3.zero; Assert.That(grabInteractable.attachPointCompatibilityMode, Is.EqualTo(XRGrabInteractable.AttachPointCompatibilityMode.Default)); // Wait for physics update to ensure the Rigidbody is stable and center of mass has been calculated yield return(new WaitForFixedUpdate()); yield return(null); Assert.That(grabInteractable.isSelected, Is.False); Assert.That(grabInteractable.attachTransform.position, Is.EqualTo(new Vector3(1f, 2f, 3f) + attachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(grabInteractable.transform.position, Is.EqualTo(new Vector3(1f, 2f, 3f)).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.transform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(boxCollider, Is.Not.Null); Assert.That(boxCollider.center, Is.EqualTo(centerOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.position, Is.EqualTo(new Vector3(1f, 2f, 3f)).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.centerOfMass, Is.EqualTo(centerOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.worldCenterOfMass, Is.EqualTo(new Vector3(1f, 2f, 3f) + centerOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); // Create Interactor at some arbitrary point away from the Interactable var interactorGO = new GameObject("Interactor"); var targetPosition = Vector3.zero; interactorGO.transform.localPosition = Vector3.zero; interactorGO.transform.localRotation = Quaternion.identity; var interactor = interactorGO.AddComponent <MockInteractor>(); yield return(null); Assert.That(grabInteractable.isSelected, Is.False); Assert.That(interactor.selectTarget, Is.Null); Assert.That(interactor.attachTransform, Is.Not.Null); Assert.That(interactor.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(interactor.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); // Set valid so it will be selected next frame by the Interaction Manager interactor.validTargets.Add(grabInteractable); yield return(WaitForSteadyState(movementType)); Assert.That(grabInteractable.isSelected, Is.True); Assert.That(grabInteractable.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(grabInteractable.transform.position, Is.EqualTo(targetPosition - attachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.transform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(interactor.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(interactor.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(rigidbody.position, Is.EqualTo(targetPosition - attachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); // Move the attach transform on the Interactor after being grabbed targetPosition = new Vector3(5f, 5f, 5f); interactor.attachTransform.position = targetPosition; yield return(WaitForSteadyState(movementType)); Assert.That(grabInteractable.isSelected, Is.True); Assert.That(grabInteractable.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(grabInteractable.transform.position, Is.EqualTo(targetPosition - attachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.transform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(interactor.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(interactor.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(rigidbody.position, Is.EqualTo(targetPosition - attachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); // Move the attach transform on the Interactable to the back lower-right corner of the cube attachOffset = new Vector3(0.5f, -0.5f, -0.5f); grabInteractable.attachTransform.localPosition = attachOffset; yield return(WaitForSteadyState(movementType)); Assert.That(grabInteractable.isSelected, Is.True); Assert.That(grabInteractable.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(grabInteractable.transform.position, Is.EqualTo(targetPosition - attachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.transform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(interactor.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(interactor.attachTransform.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); Assert.That(rigidbody.position, Is.EqualTo(targetPosition - attachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.rotation, Is.EqualTo(Quaternion.identity).Using(QuaternionEqualityComparer.Instance)); }
public IEnumerator NonCenteredObjectRotatesToExpectedOrientation([ValueSource(nameof(s_MovementTypes))] XRBaseInteractable.MovementType movementType) { // Create a cube mesh with the pivot position at the bottom center // rather than the built-in Cube resource which has its center at the // center of the cube. var cubePrimitiveGO = GameObject.CreatePrimitive(PrimitiveType.Cube); var cubePrimitiveMesh = cubePrimitiveGO.GetComponent <MeshFilter>().sharedMesh; var centerOffset = new Vector3(0f, 0.5f, 0f); var offCenterMesh = new Mesh { vertices = cubePrimitiveMesh.vertices.Select(vertex => vertex + centerOffset).ToArray(), triangles = cubePrimitiveMesh.triangles.ToArray(), normals = cubePrimitiveMesh.normals.ToArray(), }; cubePrimitiveGO.SetActive(false); Object.Destroy(cubePrimitiveGO); // Create Grab Interactable at some arbitrary point var grabInteractableGO = new GameObject("Grab Interactable"); grabInteractableGO.transform.localPosition = new Vector3(1f, 2f, 3f); grabInteractableGO.transform.localRotation = Quaternion.LookRotation(Vector3.back, Vector3.up); var meshFilter = grabInteractableGO.AddComponent <MeshFilter>(); meshFilter.sharedMesh = offCenterMesh; grabInteractableGO.AddComponent <MeshRenderer>(); var boxCollider = grabInteractableGO.AddComponent <BoxCollider>(); var rigidbody = grabInteractableGO.AddComponent <Rigidbody>(); rigidbody.useGravity = false; rigidbody.isKinematic = true; var grabInteractable = grabInteractableGO.AddComponent <XRGrabInteractable>(); grabInteractable.movementType = movementType; DisableDelayProperties(grabInteractable); // Set the Attach Transform to the back upper-right corner of the cube // to test an attach transform different from both the transform position and center. var grabInteractableAttach = new GameObject("Grab Interactable Attach").transform; var attachOffset = new Vector3(0.5f, 1f, -0.5f); grabInteractableAttach.SetParent(grabInteractable.transform); grabInteractableAttach.localPosition = attachOffset; var attachRotation = Quaternion.LookRotation(Vector3.left, Vector3.forward); grabInteractableAttach.rotation = attachRotation; grabInteractable.attachTransform = grabInteractableAttach; Assert.That(grabInteractable.attachPointCompatibilityMode, Is.EqualTo(XRGrabInteractable.AttachPointCompatibilityMode.Default)); // Wait for physics update to ensure the Rigidbody is stable and center of mass has been calculated yield return(new WaitForFixedUpdate()); yield return(null); Assert.That(grabInteractable.isSelected, Is.False); // The Grab Interactable is rotated 180 degrees around the y-axis, // so the Attach Transform becomes the front upper-left corner of the cube from the perspective of the world axes, // so the position will end up at (0.5, 3, 3.5). var worldAttachOffset = new Vector3(-0.5f, 1f, 0.5f); Assert.That(grabInteractable.attachTransform.position, Is.EqualTo(new Vector3(1f, 2f, 3f) + worldAttachOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.transform.position, Is.EqualTo(new Vector3(1f, 2f, 3f)).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(boxCollider.center, Is.EqualTo(centerOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.position, Is.EqualTo(new Vector3(1f, 2f, 3f)).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.centerOfMass, Is.EqualTo(centerOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.worldCenterOfMass, Is.EqualTo(new Vector3(1f, 2f, 3f) + centerOffset).Using(Vector3ComparerWithEqualsOperator.Instance)); // Create Interactor at some arbitrary point away from the Interactable var interactorGO = new GameObject("Interactor"); var targetPosition = Vector3.zero; var targetRotation = Quaternion.identity; interactorGO.transform.localPosition = Vector3.zero; interactorGO.transform.localRotation = Quaternion.identity; var interactor = interactorGO.AddComponent <MockInteractor>(); yield return(null); Assert.That(grabInteractable.isSelected, Is.False); Assert.That(interactor.selectTarget, Is.Null); Assert.That(interactor.attachTransform, Is.Not.Null); Assert.That(interactor.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(interactor.attachTransform.rotation, Is.EqualTo(targetRotation).Using(QuaternionEqualityComparer.Instance)); // Set valid so it will be selected next frame by the Interaction Manager interactor.validTargets.Add(grabInteractable); yield return(WaitForSteadyState(movementType)); Assert.That(grabInteractable.isSelected, Is.True); // When the Grab Interactable moves to align with the Interactor's Attach Transform at the origin, // the cube should end up with the transform pivot on the right face from the perspective of the world axes // to have the Attach Transform there pointing forward. var expectedRotation = Quaternion.LookRotation(Vector3.down, Vector3.left); Assert.That(grabInteractable.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.attachTransform.rotation, Is.EqualTo(targetRotation).Using(QuaternionEqualityComparer.Instance)); Assert.That(grabInteractable.transform.position, Is.EqualTo(new Vector3(1f, -0.5f, -0.5f)).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(grabInteractable.transform.rotation, Is.EqualTo(expectedRotation).Using(QuaternionEqualityComparer.Instance)); Assert.That(interactor.attachTransform.position, Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(interactor.attachTransform.rotation, Is.EqualTo(targetRotation).Using(QuaternionEqualityComparer.Instance)); Assert.That(rigidbody.position, Is.EqualTo(new Vector3(1f, -0.5f, -0.5f)).Using(Vector3ComparerWithEqualsOperator.Instance)); Assert.That(rigidbody.rotation, Is.EqualTo(expectedRotation).Using(QuaternionEqualityComparer.Instance)); }