public IEnumerator ForceGrab(HVRGrabbable grabbable) { var needsCollisionEnabled = true; var grabPoint = grabbable.GetForceGrabPoint(HandGrabber.HandSide) ?? grabbable.transform; try { HandGrabber.DisableHandCollision(grabbable); _grabbableCollided = false; IsHoldActive = true; grabbable.IsBeingForcedGrabbed = true; grabbable.Rigidbody.useGravity = false; grabbable.Rigidbody.drag = 0f; fts.solve_ballistic_arc_lateral(false, grabPoint.position, ForceTime, JointAnchorWorldPosition, JointAnchorWorldPosition.y + YOffset, out var velocity, out var gravity); grabbable.Rigidbody.velocity = velocity; var elapsed = 0f; var targetRotation = grabbable.GetForceGrabPointRotation(HandGrabber.HandSide); var startHandRotation = HandGrabber.HandWorldRotation; while (GrabbedTarget) { if (elapsed > ForceTime) { break; } var currentVector = JointAnchorWorldPosition - grabPoint.position; currentVector.y = 0; var percentTime = elapsed / ForceTime; var yExtra = YOffset * (1 - percentTime); if (percentTime < .3) { _grabbableCollided = false; } else if (_grabbableCollided) { if (grabbable.Rigidbody.velocity.magnitude > MaximumVelocityPostCollision) { grabbable.Rigidbody.velocity = grabbable.Rigidbody.velocity.normalized * MaximumVelocityPostCollision; } ForceRelease(); //Debug.Log($"Collided while force grabbing."); break; } fts.solve_ballistic_arc_lateral( false, grabPoint.position, ForceTime - elapsed, JointAnchorWorldPosition, JointAnchorWorldPosition.y + yExtra, out velocity, out gravity); grabbable.Rigidbody.velocity = velocity; grabbable.Rigidbody.AddForce(-Vector3.up * gravity, ForceMode.Acceleration); if (AutoGrab && (JointAnchorWorldPosition - grabPoint.position).magnitude < AutoGrabDistance) { if (HandGrabber.TryAutoGrab(GrabbedTarget)) { needsCollisionEnabled = false; IsForceGrabbing = false; break; } } if (currentVector.magnitude < .1f) { //Debug.Log($"<.1f"); break; } var handDelta = Quaternion.Angle(HandGrabber.HandWorldRotation, startHandRotation); if (Mathf.Abs(handDelta) > 20) { startHandRotation = HandGrabber.HandWorldRotation; } //todo var delta = startHandRotation * Quaternion.Inverse(targetRotation); delta.ToAngleAxis(out var angle, out var axis); if (angle > 180.0f) { angle -= 360.0f; } var remaining = ForceTime - elapsed; if (percentTime > .3f && Mathf.Abs(angle) > 1 && remaining > .01) { grabbable.Rigidbody.angularVelocity = axis * (angle * Mathf.Deg2Rad) / ForceTime; } else { grabbable.Rigidbody.angularVelocity = Vector3.zero; } elapsed += Time.fixedDeltaTime; yield return(new WaitForFixedUpdate()); } } finally { if (needsCollisionEnabled) { HandGrabber.EnableHandCollision(grabbable); } IsHoldActive = false; grabbable.IsBeingForcedGrabbed = false; grabbable.Collided.RemoveListener(OnGrabbableCollided); grabbable.Grabbed.RemoveListener(OnGrabbableGrabbed); if (IsGrabbing) { ForceRelease(); } IsForceGrabbing = false; } if (AutoGrab && AdditionalAutoGrabTime > 0 && !grabbable.IsBeingHeld) { _additionalGrabRoutine = StartCoroutine(ContinueAutoGrab(grabbable, grabPoint)); } }
public IEnumerator ForcePull(HVRGrabbable grabbable) { var rb = grabbable.Rigidbody; var drag = rb.drag; var angularDrag = rb.angularDrag; HandGrabber.DisableHandCollision(grabbable); rb.useGravity = false; rb.drag = 0f; rb.angularDrag = 0f; grabbable.IsBeingForcedGrabbed = true; IsHoldActive = true; var grabPoint = grabbable.GetForceGrabPoint(HandGrabber.HandSide) ?? grabbable.transform; var posableGrabPoint = grabPoint.GetComponent <HVRPosableGrabPoint>(); var isPhysicsGrab = grabbable.GrabType == HVRGrabType.PhysicPoser; if (!isPhysicsGrab && grabbable.GrabType != HVRGrabType.Offset) { var actualGrabPoint = HandGrabber.GetGrabPoint(grabbable); isPhysicsGrab = actualGrabPoint == null && grabbable.PhysicsPoserFallback; } var direction = HandGrabber.JointAnchorWorldPosition - grabPoint.position; var startDistance = direction.magnitude; var distance = startDistance; var settings = grabbable.ForcePullOverride ?? ForcePullSettings; var Spring = settings.Spring; var Damper = settings.Damper; var MaxForce = settings.MaxForce; var SlerpDamper = settings.SlerpDamper; var SlerpMaxForce = settings.SlerpMaxForce; var SlerpSpring = settings.SlerpSpring; var DynamicGrabThreshold = settings.DynamicGrabThreshold; var DistanceThreshold = settings.DistanceThreshold; var Speed = settings.Speed; var DistanceToRotate = settings.RotateTriggerDistance; var RotateOverDistance = settings.RotateOverDistance; var MaxMissSpeed = settings.MaxMissSpeed; var MaxMissAngularSpeed = settings.MaxMissAngularSpeed; _forceAnchor.transform.position = grabPoint.transform.position; _forceAnchor.transform.rotation = grabbable.transform.rotation; if (posableGrabPoint) { _forceAnchor.transform.rotation = posableGrabPoint.GetPoseWithJointRotation(HandSide); } var joint = _forceAnchor.AddComponent <ConfigurableJoint>(); joint.autoConfigureConnectedAnchor = false; joint.rotationDriveMode = RotationDriveMode.Slerp; joint.SetLinearDrive(Spring, Damper, MaxForce); joint.SetSlerpDrive(SlerpSpring, SlerpDamper, SlerpMaxForce); joint.connectedBody = rb; joint.connectedAnchor = rb.transform.InverseTransformPoint(grabPoint.position); var limit = isPhysicsGrab ? DynamicGrabThreshold : DistanceThreshold; var rotating = false; var rotateSpeed = 0f; var elapsed = 0f; var needsRotating = posableGrabPoint; while (GrabbedTarget && Inputs.GetForceGrabActive(HandSide) && distance > limit) { direction = HandGrabber.JointAnchorWorldPosition - grabPoint.position; distance = direction.magnitude; if ((isPhysicsGrab || grabbable.GrabType == HVRGrabType.Offset) && distance < DynamicGrabThreshold && HandGrabber.TryAutoGrab(grabbable)) { rb.angularVelocity = Vector3.zero; rb.velocity = Vector3.zero; break; } _forceAnchor.transform.position = Vector3.MoveTowards(_forceAnchor.transform.position, JointAnchorWorldPosition, Speed * Time.fixedDeltaTime); var dir = _forceAnchor.transform.position - grabPoint.position; if (dir.magnitude > .3f) { _forceAnchor.transform.position = grabPoint.position + dir.normalized * .3f; } if (needsRotating && !rotating) { if (settings.RotationTrigger == ForcePullRotationTrigger.DistanceToHand) { rotating = distance < DistanceToRotate && posableGrabPoint; } else if (settings.RotationTrigger == ForcePullRotationTrigger.PercentTraveled) { rotating = (startDistance - distance) / startDistance > settings.RotateTriggerPercent / 100f; } else if (settings.RotationTrigger == ForcePullRotationTrigger.TimeSinceStart) { rotating = elapsed > settings.RotateTriggerTime; } if (rotating) { if (settings.RotationStyle == ForceRotationStyle.RotateOverDistance) { var rotatateDistance = Mathf.Min(RotateOverDistance, distance); var time = rotatateDistance / Speed; rotateSpeed = Quaternion.Angle(joint.transform.rotation, HandGrabber.HandWorldRotation) / time; } else if (settings.RotationStyle == ForceRotationStyle.RotateOverRemaining) { var time = distance / Speed; rotateSpeed = Quaternion.Angle(joint.transform.rotation, HandGrabber.HandWorldRotation) / time; } } } if (rotating) { joint.transform.rotation = Quaternion.RotateTowards(joint.transform.rotation, HandGrabber.HandWorldRotation, rotateSpeed * Time.fixedDeltaTime); } yield return(new WaitForFixedUpdate()); elapsed += Time.fixedDeltaTime; } joint.connectedBody = null; Destroy(joint); IsForceGrabbing = false; IsHoldActive = false; if (grabbable) { rb.useGravity = true; rb.drag = drag; rb.angularDrag = angularDrag; rb.velocity = Vector3.ClampMagnitude(rb.velocity, MaxMissSpeed); rb.angularVelocity = Vector3.ClampMagnitude(rb.angularVelocity, MaxMissAngularSpeed); if (IsGrabbing) { direction = HandGrabber.JointAnchorWorldPosition - grabPoint.position; if (direction.magnitude < limit) { if (HandGrabber.TryAutoGrab(grabbable)) { rb.angularVelocity = Vector3.zero; rb.velocity = Vector3.zero; } else { HandGrabber.EnableHandCollision(grabbable); ForceRelease(); } } grabbable.IsBeingForcedGrabbed = false; } } }