Unity-Technologies / XR-Interaction-Toolkit-Examples

This repository contains various examples to use with the XR Interaction Toolkit
Other
1.12k stars 365 forks source link

[VR] Destroying an Interactable Object throws MissingReferenceException #26

Open PhiSel opened 4 years ago

PhiSel commented 4 years ago

Problem: When destroying an interactable object while the interactor is still inside the object, a MissingReferenceException is thrown:

image

Reproduction: Attach a XRGrabInteractable component and the following script to a GameObject:

image

After releasing the grab (selectExit), the object is destroyed but the exception is thrown.

Quickfix: In order to quickly fix this problem, one could change the following in XRBaseInteractable.cs:

image

Markovicho commented 4 years ago

I can confirm this investigation in terms of destroying Interactable which are still hovered in the moment of the Destroy() call. Tried also several things like disabling the collider and/or XRBaseInteractable before destroying the object with no look.

thx @PhiSel for reporting ;-)

Hugo5503 commented 4 years ago

I just exposed the UnregisterInteractable method in the XRInteractionManager.cs script. And then call it from my interactable OnDestroy

nuehado commented 4 years ago

How did you go about exposing the internal method without it getting overwritten when closing/reopening the project?

Hugo5503 commented 4 years ago

I added the edited files to my version control. Everytime I open the project Unity removes my changes but I just discard the changes in version control.

staakman commented 4 years ago

You can get around the limitation with reflection although personally I think it's better to write a object pooler.

Reflection example (put it on the same object as the XRInteractionManager):

using UnityEngine.XR.Interaction.Toolkit;
using System.Reflection;

public class XRInteractionManagerExposed : MonoBehaviour
{
    private static XRInteractionManagerExposed instance = null;
    private XRInteractionManager manager = null;
    private MethodInfo unregisterInteractable = null;

    public static XRInteractionManagerExposed GetInstance()
    {
        return instance;
    }

    private void Start()
    {
        instance = this;
        manager = this.GetComponent<XRInteractionManager>();

        System.Type t = typeof(XRInteractionManager);

        MethodInfo[] methods = t.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
        foreach (MethodInfo mi in methods)
        {
            if (mi.Name == "UnregisterInteractable")
            {
                unregisterInteractable = mi;
                break; //leave the loop
            }
        }
    }

    public void UnregisterInteractable(XRBaseInteractable interactable)
    {
        object[] args = new Object[] { interactable };
        unregisterInteractable.Invoke(manager, args);
    }
}
Peaceteddie commented 4 years ago

Here is something I tried and seems to be working so far: GetComponent<XRGrabInteractable>().colliders.Clear(); Seems to unregister the collider with the interaction manager before the destruction of the gameobject Also think XRGrabInteractable can be substituted with what ever XRBaseInteractable script you have, but I'm using grab.

Hugo5503 commented 3 years ago

@Peaceteddie I'm upgrading my project to XR 0.10.0 and it works. Cheers!

marekklofac commented 3 years ago

As @Peaceteddie said, adding gameObject.GetComponent<XRGrabInteractable>().colliders.Clear(); before the deletion of given GameObject works. Have not encountered any errors since.

wojiuxuihuan commented 12 months ago

Try this if you don't want to use Reflection, just create a newer script then replace the old one on Inpsector[Debug Mode], works for XRSocketInteractor too:

public class XRGrabInteractableEs : XRGrabInteractable
{
    protected override void OnDestroy()
    {
        base.OnDestroy();
        OnDisable();//Call UnregisterWithInteractionManager
    }
    ...
}