ThrowableObject FindNearestObject(ThrowableObject[] objects, Vector3 position) { ThrowableObject nearestObject = null; float lowestDist = Mathf.Infinity; foreach (var throwable in objects) { foreach (var collider in throwable.colliders) { float dist = Vector3.Distance(position, collider.ClosestPoint(position)); if (dist < GRAB_RANGE && dist < lowestDist) { lowestDist = dist; nearestObject = throwable; } } } return(nearestObject); }
// For each given hand: Grab, Move, or Release a GameObject void UpdateForHand(XRNode handNode, HandData handData) { InputDevice device = InputDevices.GetDeviceAtXRNode(handNode); bool deviceHasData = device.TryGetFeatureValue(CommonUsages.isTracked, out bool deviceIsTracked); bool? isGrabbing = TryGetIsGrabbing(device); bool isDeviceTapped = isGrabbing ?? false; deviceHasData &= (isGrabbing != null); deviceHasData &= device.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 devicePosition); deviceHasData &= device.TryGetFeatureValue(CommonUsages.deviceRotation, out Quaternion deviceRotation); if (!deviceHasData || !deviceIsTracked) { return; } // Update the hand's transform for use below handData.transform.SetPositionAndRotation(devicePosition, deviceRotation); // If this hand should grab an object, look for one nearby if (handData.objectHeld == null && isDeviceTapped && !handData.wasTapped) { ThrowableObject nearestObject = FindNearestObject(m_throwableObjects, devicePosition); if (nearestObject != null) { // Grab the object handData.objectHeld = new ObjectHeldData { rigidbody = nearestObject.GetComponent <Rigidbody>() }; handData.objectHeld.rigidbody.isKinematic = true; // Record the offset from the hand pose to the object origin pose handData.objectHeld.positionInHandSpace = handData.transform.InverseTransformPoint(handData.objectHeld.rigidbody.position); handData.objectHeld.rotationInHandSpace = Quaternion.Inverse(deviceRotation) * handData.objectHeld.rigidbody.rotation; } } // If this hand is holding an object, update its pose. if (handData.objectHeld != null) { handData.objectHeld.rigidbody.position = handData.transform.TransformPoint(handData.objectHeld.positionInHandSpace); handData.objectHeld.rigidbody.rotation = deviceRotation * handData.objectHeld.rotationInHandSpace; } // If this hand should release the object, derive the new object velocity using the current hand velocity and relative object pose. if (handData.objectHeld != null && !isDeviceTapped) { // Get the device's velocity and angular velocity bool hasData = device.TryGetFeatureValue(CommonUsages.deviceVelocity, out Vector3 deviceVelocity); hasData &= device.TryGetFeatureValue(CommonUsages.deviceAngularVelocity, out Vector3 deviceAngularVelocity); if (!hasData) { deviceVelocity = Vector3.zero; deviceAngularVelocity = Vector3.zero; } // As the object is released, the hand and object share an angular velocity. handData.objectHeld.rigidbody.angularVelocity = deviceAngularVelocity; // As the object is released, we apply two velocities - the velocity of the hand relative to the world, // and the velocity of the object's center of mass relative to the hand. handData.objectHeld.rigidbody.velocity = deviceVelocity + Vector3.Cross(handData.objectHeld.rigidbody.angularVelocity, handData.objectHeld.rigidbody.worldCenterOfMass - devicePosition); handData.objectHeld.rigidbody.isKinematic = false; handData.objectHeld = null; } handData.wasTapped = isDeviceTapped; }