Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

XR Rig - Grab from touchpoint, not rigidbody center

Discussion in 'Scripting' started by gibsondude78, Apr 29, 2020.

  1. gibsondude78

    gibsondude78

    Joined:
    Aug 12, 2017
    Posts:
    4
    I have a scene set up which includes a table with various different game objects sat on it, all of which need to be interactable by the player.
    Each game object has an XR Grab Interactable script attached, and both controllers have a custom hand pointer script (basically an OnTriggerEnter/Exit function which sets 2 boolean values on another custom locomotion script attached to the XRRig game object, LeftContactHit and RightContactHit, this basically stops the player from dragging the scene around when the "pointers" are in contact with any other game object), both controllers also have an XR Direct Interactor script attached.
    I'm using a custom model for both hand controllers, which include a sphere as a "pointer" which is set to (0, 0.03, 0.1) at the point of origin of each controller, and a sphere collider (set as a trigger), also set to the same point.

    So the locomotion script allows the player to press the trigger on either controller to grab empty space and pull the rig around. Pointing both controllers upward and pressing both triggers together and pulling apart/pushing together scales the world and pointing both controllers forwards and pressing both triggers rotates around the midpoint of each controller location... This is all working great...

    Now, when I grab an Interactable it disables my locomotion script - as intended.

    I want to be able to grab the interactable and set its "Attach Transform" location to where the sphere collider hits it from... not rigidbody center... Any ideas anyone?

    Actually, I want more of a precision grab, I've just noticed the interactable snaps to the controller rotation :/

    **Edit**
    I've found a solution, for anyone else interested I've used the following script attached to the grabbable:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.XR.Interaction.Toolkit;
    6.  
    7. /// <summary>
    8. /// Small modification of the classic XRGrabInteractable that will keep the position and rotation offset between the
    9. /// grabbed object and the controller instead of snapping the object to the controller. Better for UX and the illusion
    10. /// of holding the thing (see Tomato Presence : https://owlchemylabs.com/tomatopresence/)
    11. /// </summary>
    12. public class XROffsetGrabbable : XRGrabInteractable
    13. {
    14.     class SavedTransform
    15.     {
    16.         public Vector3 OriginalPosition;
    17.         public Quaternion OriginalRotation;
    18.     }
    19.    
    20.    
    21.     Dictionary<XRBaseInteractor, SavedTransform> m_SavedTransforms = new Dictionary<XRBaseInteractor, SavedTransform>();
    22.  
    23.     Rigidbody m_Rb;
    24.  
    25.     protected override void Awake()
    26.     {
    27.         base.Awake();
    28.  
    29.         //the base class already grab it but don't expose it so have to grab it again
    30.         m_Rb = GetComponent<Rigidbody>();
    31.     }
    32.  
    33.     protected override void OnSelectEnter(XRBaseInteractor interactor)
    34.     {
    35.         if (interactor is XRDirectInteractor)
    36.         {
    37.             SavedTransform savedTransform = new SavedTransform();
    38.            
    39.             savedTransform.OriginalPosition = interactor.attachTransform.localPosition;
    40.             savedTransform.OriginalRotation = interactor.attachTransform.localRotation;
    41.  
    42.             m_SavedTransforms[interactor] = savedTransform;
    43.            
    44.            
    45.             bool haveAttach = attachTransform != null;
    46.  
    47.             interactor.attachTransform.position = haveAttach ? attachTransform.position : m_Rb.worldCenterOfMass;
    48.             interactor.attachTransform.rotation = haveAttach ? attachTransform.rotation : m_Rb.rotation;
    49.         }
    50.  
    51.         base.OnSelectEnter(interactor);
    52.     }
    53.  
    54.     protected override void OnSelectExit(XRBaseInteractor interactor)
    55.     {
    56.         if (interactor is XRDirectInteractor)
    57.         {
    58.             SavedTransform savedTransform = null;
    59.             if (m_SavedTransforms.TryGetValue(interactor, out savedTransform))
    60.             {
    61.                 interactor.attachTransform.localPosition = savedTransform.OriginalPosition;
    62.                 interactor.attachTransform.localRotation = savedTransform.OriginalRotation;
    63.  
    64.                 m_SavedTransforms.Remove(interactor);
    65.             }
    66.         }
    67.        
    68.         base.OnSelectExit(interactor);
    69.     }
    70.  
    71.     public override bool IsSelectableBy(XRBaseInteractor interactor)
    72.     {
    73.         int interactorLayerMask = 1 << interactor.gameObject.layer;
    74.         return base.IsSelectableBy(interactor) && (interactionLayerMask.value & interactorLayerMask) != 0 ;
    75.     }
    76. }
    77.  
     
    Last edited: May 6, 2020
  2. pankao

    pankao

    Joined:
    Feb 18, 2013
    Posts:
    12
    Man I was thinking exactly the same way, tkanks a bunch for the script!
     
  3. Riiich

    Riiich

    Joined:
    Sep 30, 2014
    Posts:
    18
    This code is no longer working. I've created an updated script with the same functionality here: https://github.com/richardmuthwill/Unity-Snippets/blob/main/XR/XROffsetGrabbable.cs

    Here's the code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.XR.Interaction.Toolkit;
    3.  
    4. /// <summary>
    5. /// Copyright (c) 2022 by Richard Muthwill. Update and repost this script as you wish but leave this line as is.
    6. ///
    7. /// This script will keep the object in place when grabbing. This gives the illusion of actually
    8. /// grabbing an object instead of it flying into the middle of the rigidbody or attach point.
    9. ///
    10. /// If you are using something other than XRGrabInteractable or a script that derives from
    11. /// it, please change all the XRGrabInteractable to YourXRGrabInteractableDerivedClass
    12. ///
    13. /// Original idea / answer from Unity Forums: https://forum.unity.com/threads/xr-rig-grab-from-touchpoint-not-rigidbody-center.879037/
    14. ///
    15. /// Copy of original file: https://github.com/richardmuthwill/Unity-Snippets/blob/main/XR/XROffsetGrabbableObsolete.cs
    16. /// </summary>
    17.  
    18. [DisallowMultipleComponent]
    19. [RequireComponent(typeof(XRGrabInteractable))]
    20. public class XROffsetGrabbable : MonoBehaviour
    21. {
    22.     XRGrabInteractable grabInteractable;
    23.     Transform attachPoint;
    24.  
    25.     void Awake()
    26.     {
    27.         grabInteractable = GetComponent<XRGrabInteractable>();
    28.  
    29.         if (grabInteractable.attachTransform == null)
    30.         {
    31.             GameObject newGO = new GameObject("Attach Transform of " + name);
    32.             Transform newTransform = newGO.transform;
    33.             newTransform.parent = transform;
    34.  
    35.             grabInteractable.attachTransform = newTransform;
    36.             attachPoint = newTransform;
    37.         }
    38.     else
    39.         {
    40.             attachPoint = grabInteractable.attachTransform;
    41.         }
    42.     }
    43.  
    44.     void OnEnable()
    45.     {
    46.         grabInteractable.selectEntered.AddListener(XRSelectEnter);
    47.     }
    48.  
    49.     void OnDisable()
    50.     {
    51.         grabInteractable.selectEntered.RemoveListener(XRSelectEnter);
    52.     }
    53.  
    54.     public void XRSelectEnter(SelectEnterEventArgs selectEnterEventArgs)
    55.     {
    56.         attachPoint.position = selectEnterEventArgs.interactorObject.transform.position;
    57.         attachPoint.rotation = selectEnterEventArgs.interactorObject.transform.rotation;
    58.     }
    59. }