Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to manually release grabbed object? #29

Open
everystone opened this issue Mar 25, 2020 · 17 comments
Open

How to manually release grabbed object? #29

everystone opened this issue Mar 25, 2020 · 17 comments

Comments

@everystone
Copy link

How can I force the XRDirectInteractor to release its held interactable?

@PicoPlanetDev
Copy link

I would like to know, too.

@nuehado
Copy link

nuehado commented Jun 9, 2020

Thirding this one

@PicoPlanetDev
Copy link

PicoPlanetDev commented Jun 9, 2020

Try this:
`
Yeah, I've done a force de-select.
``
1
[2:36 PM]
I just make them public.
public new void OnSelectEnter(XRBaseInteractor interactor)
{
base.OnSelectEnter(interactor);
}

public new void OnSelectExit(XRBaseInteractor interactor)
{
    base.OnSelectExit(interactor);
}`

[2:37 PM]
Then on the interactor I just have this.
private void ForceDeselect()
{
base.OnSelectExit(currentObject);
currentObject.OnSelectExit(this);
}
`
This inherits from the class.
This is quoted from VR With Andrew's Discord server. Andrew (C-Through#7567) explicitly gave me permission to post his answer here.

@dre4mer12345
Copy link

@PicoPlanetDev I've been trying to use what you described here to force a release but haven't been able to do it. Could you possibly clarify what you did a little more? I'd be very greatful!

I'm making the void public in the grab intractable script. That's straight forward enough but when you say "on the interactor" i'm not sure if that is on a script on the object getting released and if it's supposed to be in the XRgrabinteractor class or monobehavior class. So far I've been messing with it for a while and can't seem to get it to work.

@iangiblin
Copy link

iangiblin commented Aug 9, 2020

Even with all this help above it took me a while to muddle through it. So for the benefit of anyone else coming here or maybe @PicoPlanetDev, this is my solution for dropping an item in code:

In XRBaseInteractable I added this public method (just after OnSelectExit, if it matters):

        /// <summary>This method was added by ian to enable dropping of objects 
        /// when we need to do it in code (e.g. at level end or when exhausted)
        /// </summary>
        /// <param name="interactor">Usually the player's hand</param>
        public void CustomForceDrop(XRBaseInteractor interactor)
        {
            OnSelectExit(interactor);
        }

Then in the code on the interactable item (in my case a knife) I added this:

    _xrGrabInteractable = GetComponent<XRGrabInteractable>();
    XRBaseInteractor _beingHeldBy = _xrGrabInteractable.selectingInteractor;
    Debug.Log($"{this} is being held by {_beingHeldBy}");
    _xrGrabInteractable.CustomForceDrop(_beingHeldBy);

Now for someone else's use case you may want to set the object inactive, or have a time to disable the XRGrabInteractable component to avoid the object automatically being picked back up by the Interactor (hand). In my case I want to remove the object - a powerup or food for example - from the player's hand, so I call gameObject.SetActive(false); immediately after the CustomForceDrop.

Also note: If you update Unity XR Interactions on the next release, I think the CustomForceDrop method will be lost and you'll have to replace it, if it's needed. I am using XR Interaction Toolkit 0.9.4-preview.

Edit later: I found that in Unity 2020.1.2 (and maybe others) the package gets "fixed" every time I task back to Unity so the only way to hold onto changes is to copy the package as described here:
https://forum.unity.com/threads/how-can-i-edit-a-script-in-a-package-without-it-being-overwritten-when-opening-unity.734000/#post-4895588

@Gusinuhe
Copy link

We implemented a solution that forces the intractable to stop interacting at all for a frame, this makes it get released, take a look at:

https://github.com/Innoactive/XR-Interaction-Component/blob/16b12e9ffca584d56054fb001a80419cbb01969b/Runtime/Interaction/InteractableObject.cs#L105-L117

And

https://github.com/Innoactive/XR-Interaction-Component/blob/16b12e9ffca584d56054fb001a80419cbb01969b/Runtime/Interaction/InteractableObject.cs#L182-L208

We are considering to stop individual interactions in a near future.

@lucafro
Copy link

lucafro commented Oct 20, 2020

I used the approach by @iangiblin above. The custom force drop worked for me. In my use case i disable my interactable gameobject right after force releasing it. However, when I do this, for some reason the interactable stays in the hover state and as soon as I press the trigger it snaps back to the interactor hand and is selected again, although the interactable gameobject is disabled. This is an issue because the interactor is now blocked from picking up any interactable.

I solved this issue by setting the interaction layer mask on the interactable to 0, right before i disable the interactable.

_grabInteractable = GetComponent<XRGrabInteractable>();
_grabInteractable.interactionLayerMask = 0;

I assume that when this is called, the InteractionManager removes the interactable/unregisters it from any hand causing the hover and select state to be cleared. Correct me if I'm wrong. Definitely hacky, worked for me though.

@Dyzalonius
Copy link

I would love a first party fix for this!

@Holi0317
Copy link

Starting from 0.10.0 there are SelectCancel, SelectExit methods on XRInteractionManager for deselecting.

Starting from 1.0.0 pre there are CancelInteractableSelection and CancelInteractorSelection.

I have add extension methods to interactor and interactable to wrap the cancel call. They should work with 1.0.0 pre or later.

public static class XRBaseInteractableExtension
{
    /// <summary>
    /// Force deselect the selected interactable.
    ///
    /// This is an extension method for <c>XRBaseInteractable</c>.
    /// </summary>
    /// <param name="interactable">Interactable that has been selected by some interactor</param>
    public static void ForceDeselect(this XRBaseInteractable interactable)
    {
        interactable.interactionManager.CancelInteractableSelection(interactable);
        Assert.IsFalse(interactable.isSelected);
    }
}

public static class XRBaseInteractorExtension
{
    /// <summary>
    /// Force deselect any selected interactable for given interactor.
    ///
    /// This is an extension method for <c>XRBaseInteractor</c>.
    /// </summary>
    /// <param name="interactor">Interactor that has some interactable selected</param>
    public static void ForceDeselect(this XRBaseInteractor interactor)
    {
        interactor.interactionManager.CancelInteractorSelection(interactor);
        Assert.IsFalse(interactor.isSelectActive);
    }
}

@VisComKreiser
Copy link

Starting from 0.10.0 there are SelectCancel, SelectExit methods on XRInteractionManager for deselecting.

Starting from 1.0.0 pre there are CancelInteractableSelection and CancelInteractorSelection.

I have add extension methods to interactor and interactable to wrap the cancel call. They should work with 1.0.0 pre or later.

public static class XRBaseInteractableExtension
{
    /// <summary>
    /// Force deselect the selected interactable.
    ///
    /// This is an extension method for <c>XRBaseInteractable</c>.
    /// </summary>
    /// <param name="interactable">Interactable that has been selected by some interactor</param>
    public static void ForceDeselect(this XRBaseInteractable interactable)
    {
        interactable.interactionManager.CancelInteractableSelection(interactable);
        Assert.IsFalse(interactable.isSelected);
    }
}

public static class XRBaseInteractorExtension
{
    /// <summary>
    /// Force deselect any selected interactable for given interactor.
    ///
    /// This is an extension method for <c>XRBaseInteractor</c>.
    /// </summary>
    /// <param name="interactor">Interactor that has some interactable selected</param>
    public static void ForceDeselect(this XRBaseInteractor interactor)
    {
        interactor.interactionManager.CancelInteractorSelection(interactor);
        Assert.IsFalse(interactor.isSelectActive);
    }
}

Thanks for the Extension Classes, where do you put them to be able to use them? Adding the methods to the XSBaseInteractable does not work as it is abstract.

@Holi0317
Copy link

Thanks for the Extension Classes, where do you put them to be able to use them? Adding the methods to the XRBaseInteractable does not work as it is abstract.

I think C#'s extension method works on abstract classes. Maybe you tried to call the extension method on the XRBaseInteractable class instead of the instance? My IDE shows the extension method can be called:

image

You can read more about extension method on abstract class here: https://stackoverflow.com/a/17546768

@VisComKreiser
Copy link

Oh, i have to use the whole classes XRBaseInteractableExtension and XRBaseInteractorExtension. I thoght i just have to copy the methods into the already existing ones. Thanks for clarifying, i'll definitely read up on extension classes.
Thank you very much!

@VisComKreiser
Copy link

Hey, I've managed to try this solution, but the interactor (XRSocketInteractor) directly grabs onto the object again after i've deselected it. Moving it immediately after deselection does not work either, it snaps back anyways.
There is also a ClearInteractorSelection() which i've tried separately or in conjunction with the CancelInteractorSelection(), but without any luck.
Am I still doing something wrong maybe? Do you have an idea what might be missing?

@ariensaenger
Copy link

I tried your solution Holi0317 on my mobile AR, but it just won't unselect the object.
From the SelectEnter Event I have these two variables

private IXRSelectInteractor arg1;
private IXRSelectInteractable arg2;

And then I call a method with these lines:

XRBaseInteractableExtension.ForceDeselect(arg2 as XRBaseInteractable);
XRBaseInteractorExtension.ForceDeselect(arg1 as XRBaseInteractor);

Did I missed something?

@MekalBoy
Copy link

MekalBoy commented Feb 18, 2024

Apologies for necro'ing the thread, though as stated above by @lucafro one can drop a held interactable by forcing the interaction layer mask to be different from the interactor's before cancelling selection, at least on version 2.5.2 of XR Interaction Toolkit.

In the Editor, you may want to edit the interactor's Interaction Layer Mask to only interact with certain layers, i.e. "ActiveGrab". Set the interactable's layer mask to also contain "ActiveGrab", such that it can be picked up.

For the code part:

// In my case, the interactable is set in the Editor
[SerializeField] private XRGrabInteractable grabInteractable;

Inside the method for force-dropping:

// Set the interaction layer to something that is on a different interaction layer than the interactor's
// This prevents the interactor from immediately picking the object back up after it's force-dropped
grabInteractable.interactionLayers = InteractionLayerMask.GetMask("InertLayer");
// Cancel the selection (this is the force-drop part, taken from @Holi0317 's extension method)
// In my case, a cast is needed to force the usage of the non-deprecated method
grabInteractable.interactionManager.CancelInteractableSelection((IXRSelectInteractable) grabInteractable);
Assert.IsFalse(grabInteractable.isSelected);

Of course, you may want to re-enable the interaction later at some point. If that is the case, you can simply set the interactable's interaction layer mask back to what it was before.

grabInteractable.interactionLayers = InteractionLayerMask.GetMask("ActiveGrab");

I hope this helps!

@iangiblin
Copy link

iangiblin commented Feb 18, 2024

Interesting approach @MekalBoy thank you. I am currently just doing this, which works fine AFAIK:

        // if a held object, drop it
        var grabbable = GetComponent<XRGrabInteractable>();
        if (grabbable != null && grabbable.isSelected) { grabbable.enabled = false; }

You still have to re-enable it if you want the object to be grabbable later.

@wave-rider
Copy link

Setting grabbable.isSelected to false and re-enabling it in Update worked perfectly for me @MekalBoy @iangiblin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests