//==================================================================================== // // Purpose: Provide ability to grab an interactable object when it is being touched // // This script must be attached to a Controller within the [CameraRig] Prefab // // The SteamVR_ControllerEvents and SteamVR_InteractTouch scripts must also be // attached to the Controller // // Press the default 'Trigger' button on the controller to grab an object // Released the default 'Trigger' button on the controller to drop an object // //==================================================================================== using UnityEngine; using System.Collections; public class SteamVR_InteractGrab : MonoBehaviour { public Rigidbody controllerAttachPoint = null; public bool hideControllerOnGrab = false; public float hideControllerDelay = 0f; public event ObjectInteractEventHandler ControllerGrabInteractableObject; public event ObjectInteractEventHandler ControllerUngrabInteractableObject; Joint controllerAttachJoint; GameObject grabbedObject = null; SteamVR_InteractTouch interactTouch; SteamVR_TrackedObject trackedController; SteamVR_ControllerActions controllerActions; private int grabEnabledState = 0; public virtual void OnControllerGrabInteractableObject(ObjectInteractEventArgs e) { if (ControllerGrabInteractableObject != null) ControllerGrabInteractableObject(this, e); } public virtual void OnControllerUngrabInteractableObject(ObjectInteractEventArgs e) { if (ControllerUngrabInteractableObject != null) ControllerUngrabInteractableObject(this, e); } public void ForceRelease() { if (grabbedObject.GetComponent().AttatchIsTrackObject()) { UngrabTrackedObject(); } else { ReleaseObject((uint)trackedController.index, false); } } private void Awake() { if (GetComponent() == null) { Debug.LogError("SteamVR_InteractGrab is required to be attached to a SteamVR Controller that has the SteamVR_InteractTouch script attached to it"); return; } interactTouch = GetComponent(); trackedController = GetComponent(); controllerActions = GetComponent(); } private void Start() { //If no attach point has been specified then just use the tip of the controller if (controllerAttachPoint == null) { controllerAttachPoint = transform.GetChild(0).Find("tip").GetChild(0).GetComponent(); } if (GetComponent() == null) { Debug.LogError("SteamVR_InteractGrab is required to be attached to a SteamVR Controller that has the SteamVR_ControllerEvents script attached to it"); return; } GetComponent().AliasGrabOn += new ControllerClickedEventHandler(DoGrabObject); GetComponent().AliasGrabOff += new ControllerClickedEventHandler(DoReleaseObject); } private bool IsObjectGrabbable(GameObject obj) { return (interactTouch.IsObjectInteractable(obj) && obj.GetComponent().isGrabbable); } private bool IsObjectHoldOnGrab(GameObject obj) { return (obj && obj.GetComponent() && obj.GetComponent().holdButtonToGrab); } private void SnapObjectToGrabToController(GameObject obj) { //Pause collisions (if allowed on object) for a moment whilst sorting out position to prevent clipping issues obj.GetComponent().PauseCollisions(0.2f); SteamVR_InteractableObject.GrabSnapType grabType = obj.GetComponent().grabSnapType; if (grabType == SteamVR_InteractableObject.GrabSnapType.Rotation_Snap) { // Identity Controller Rotation this.transform.eulerAngles = new Vector3(0f, 270f, 0f); obj.transform.eulerAngles = obj.GetComponent().snapToRotation; } if (grabType != SteamVR_InteractableObject.GrabSnapType.Precision_Snap) { obj.transform.position = controllerAttachPoint.transform.position + obj.GetComponent().snapToPosition; } CreateJoint(obj); } private void CreateJoint(GameObject obj) { if (obj.GetComponent().grabAttatchMechanic == SteamVR_InteractableObject.GrabAttatchType.Fixed_Joint) { controllerAttachJoint = obj.AddComponent(); } else if (obj.GetComponent().grabAttatchMechanic == SteamVR_InteractableObject.GrabAttatchType.Spring_Joint) { SpringJoint tempSpringJoint = obj.AddComponent(); tempSpringJoint.spring = obj.GetComponent().springJointStrength; tempSpringJoint.damper = obj.GetComponent().springJointDamper; controllerAttachJoint = tempSpringJoint; } controllerAttachJoint.breakForce = obj.GetComponent().detatchThreshold; controllerAttachJoint.connectedBody = controllerAttachPoint; } private Rigidbody ReleaseGrabbedObjectFromController(bool withThrow) { var jointGameObject = controllerAttachJoint.gameObject; var rigidbody = jointGameObject.GetComponent(); if (withThrow) { Object.DestroyImmediate(controllerAttachJoint); } else { Object.Destroy(controllerAttachJoint); } controllerAttachJoint = null; return rigidbody; } private void ThrowReleasedObject(Rigidbody rb, uint controllerIndex) { var origin = trackedController.origin ? trackedController.origin : trackedController.transform.parent; var device = SteamVR_Controller.Input((int)controllerIndex); if (origin != null) { rb.velocity = origin.TransformVector(device.velocity); rb.angularVelocity = origin.TransformVector(device.angularVelocity); } else { rb.velocity = device.velocity; rb.angularVelocity = device.angularVelocity; } rb.maxAngularVelocity = rb.angularVelocity.magnitude; } private void GrabInteractedObject() { if (controllerAttachJoint == null && grabbedObject == null && IsObjectGrabbable(interactTouch.GetTouchedObject())) { InitGrabbedObject(); SnapObjectToGrabToController(grabbedObject); } } private void GrabTrackedObject() { if (grabbedObject == null && IsObjectGrabbable(interactTouch.GetTouchedObject())) { InitGrabbedObject(); } } private void InitGrabbedObject() { grabbedObject = interactTouch.GetTouchedObject(); OnControllerGrabInteractableObject(interactTouch.SetControllerInteractEvent(grabbedObject)); grabbedObject.GetComponent().Grabbed(this.gameObject); if (grabbedObject) { grabbedObject.GetComponent().ToggleHighlight(false); } if (hideControllerOnGrab) { Invoke("HideController", hideControllerDelay); } } private void HideController() { if(grabbedObject != null) { controllerActions.ToggleControllerModel(false); } } private void UngrabInteractedObject(uint controllerIndex, bool withThrow) { if (grabbedObject != null && controllerAttachJoint != null) { Rigidbody releasedObjectRigidBody = ReleaseGrabbedObjectFromController(withThrow); if (withThrow) { ThrowReleasedObject(releasedObjectRigidBody, controllerIndex); } InitUngrabbedObject(); } } private void UngrabTrackedObject() { if (grabbedObject != null) { InitUngrabbedObject(); } } private void InitUngrabbedObject() { OnControllerUngrabInteractableObject(interactTouch.SetControllerInteractEvent(grabbedObject)); grabbedObject.GetComponent().Ungrabbed(this.gameObject); if (hideControllerOnGrab) { controllerActions.ToggleControllerModel(true); } grabbedObject.GetComponent().ToggleHighlight(false); grabbedObject = null; } private void ReleaseObject(uint controllerIndex, bool withThrow) { UngrabInteractedObject(controllerIndex, withThrow); grabEnabledState = 0; } private bool IsValidGrab() { GameObject obj = interactTouch.GetTouchedObject(); return (obj != null && interactTouch.IsObjectInteractable(obj)); } private void DoGrabObject(object sender, ControllerClickedEventArgs e) { if (IsValidGrab()) { if (interactTouch.GetTouchedObject().GetComponent().AttatchIsTrackObject()) { GrabTrackedObject(); } else { GrabInteractedObject(); } if(!IsObjectHoldOnGrab(interactTouch.GetTouchedObject())) { grabEnabledState++; } } } private void DoReleaseObject(object sender, ControllerClickedEventArgs e) { if (IsObjectHoldOnGrab(grabbedObject) || grabEnabledState >= 2) { if (grabbedObject.GetComponent().AttatchIsTrackObject()) { UngrabTrackedObject(); } else { ReleaseObject(e.controllerIndex, true); } } } }