SteamVR 2.0 how to implement distance grab?

I am trying to create my own scripts called DistanceGrabber for my hands and DistanceGrabbable for the objects I want to be able to grab from a distance.
what I made kind of works but the laser pointer script is making me have to release the grip button first before executing the onDistanceGrab function in DistanceGrabbable script, so I had to “double click” rapidly and hold the grip the second time to be able to hold the object in the hand or else it will just fall to the ground.

and also

how do you disable the line when the laser is not pointing at a grabbable object?


My setup:

Unity 2019.2.6f1

SteamVR 2.0

Oculus Rift


DistanceGrabber script:

public class DistanceGrabber : MonoBehaviour
{
    private SteamVR_LaserPointer laserPointer;
    private Hand hand;

    void Awake()
    {
        laserPointer = GetComponent<SteamVR_LaserPointer>();
        hand = GetComponent<Hand>();

        laserPointer.PointerIn += PointerInside;
        laserPointer.PointerOut += PointerOutside;
        laserPointer.PointerClick += PointerClick;

        //interactable = GetComponent<Interactable>();
    }

    public void PointerClick(object sender, PointerEventArgs e)
    {
        if (e.target.GetComponent<DistanceGrabbable>() != null)
        {
            e.target.GetComponent<DistanceGrabbable>().onDistanceGrab(hand);
        }
    }

    public void PointerInside(object sender, PointerEventArgs e)
    {
        if (e.target.GetComponent<Interactable>() != null)
        {
            hand.ShowGrabHint();
            hand.IsStillHovering(e.target.GetComponent<Interactable>());
            laserPointer.active = true;
        }
    }

    public void PointerOutside(object sender, PointerEventArgs e)
    {
        if (e.target.GetComponent<Interactable>() == null)
        {
            hand.HideGrabHint();
            laserPointer.active = false;
        }
    }

DistanceGrabbable script:

public class DistanceGrabbable : MonoBehaviour
{
    protected Rigidbody rb;
    protected bool originalKinematicState;
    protected Transform originalParent;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
        originalParent = transform.parent;

        originalKinematicState = rb.isKinematic;
    }

    public void onDistanceGrab(Hand hand)
    {
        rb.isKinematic = true;
        transform.SetParent(hand.gameObject.transform);
        hand.AttachObject(gameObject, GrabTypes.Trigger);
    }

    private void OnDetachedFromHand(Hand hand)
    {
        rb.isKinematic = false;
        transform.SetParent(originalParent);
    }
}

@HelloPeopIe I have a solution to your problem. My code doesn’t use a visible laser pointer, but instead, the objects I am pointing at have a child object with a SteamVR hover highlight material, as shown in the screenshot below.

but once you have that cube as a child of your base object, here is the code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR.InteractionSystem;
using Valve.VR;

public class DistanceGrab : MonoBehaviour
{
    public Transform pointer;//the transform the laser starts at
    public LayerMask thingsWeCanGrab;//things we can grab

    Hand hand;//our hand
    bool isAttached = false;//do we have something in our hand?
    GameObject attachedObject = null;//what do we have in our hand
    Blank blank;//blank script

    // Start is called before the first frame update
    void Start()
    {
        hand = GetComponent<Hand>();//get our hand
    }
    // Update is called once per frame
    void Update()
    {
        //raycast and check if our hand is empty
        RaycastHit hit;
        if (Physics.Raycast(pointer.position, pointer.forward, out hit, 10f, thingsWeCanGrab) && hand.currentAttachedObject == null)
        {
            Interactable interactable = hit.collider.gameObject.GetComponent<Interactable>();
            SteamVR_Input_Sources source = hand.handType;
            //are we pressing grip and trigger?
            if (hand.grabGripAction[source].state == true && hand.grabPinchAction[source].state == true)
            {
                //does the interactable component exist?
                if (interactable != null)
                {
                    //move the object to your hand
                    interactable.transform.LookAt(transform);
                    interactable.gameObject.GetComponent<Rigidbody>().AddRelativeForce(Vector3.forward * 500, ForceMode.Force);
                    attachedObject = interactable.gameObject;
                    isAttached = true;
                    //attaching to hand is in the late update function
                }
            }
            blank = hit.collider.gameObject.GetComponentInChildren<Blank>();
            blank.gameObject.GetComponent<MeshRenderer>().enabled = true;
        }
        else
        {
            blank.gameObject.GetComponent<MeshRenderer>().enabled = false;
        }
    }
    private void LateUpdate()
    {
        //did we get an object to our hand during this update?
        if (isAttached)
        {
            //attach the object
            hand.AttachObject(attachedObject, GrabTypes.Grip);
            attachedObject = null;
            isAttached = false;
        }
    }
}

@HelloPeopIe
Hey man your code helped me earlier when I tried to implement the same thing. However after testing your code I found that the laser pointer had a lot of issues and I eventually came up with the same conclusion as rgbeans that a Raycast hit would work better.

However I’m assuming you have included the SteamVR_LaserPointer.cs as a laser to guide the user. Unfortunately turning the script on or off is a hassle and doesn’t really work:

https://answers.unity.com/questions/1629859/how-can-i-disable-the-steamvr-laser-how-can-i-disa.html

The solution around this would be to change the thickness of the laser when the raycast hits the specific object.
For example:

if (Physics.Raycast() hits the object) or public void PointerInside(object sender, PointerEventArgs e)
{
    laserPointer.thickness = 0.002f;
}
else
{
    laserPointer.thickness = 0;
}

If you need anymore help hmu.