I'm trying to get XR Grabbable objects networked in Unity, with the networking framework Mirror.

This should be a simple task as Mirror has a component called "NetworkTransform" that simply helps sync the position of an object on both the server and the client.

The issue is that the XR Interaction Manager that Unity uses switches the parent of the held object, and moves it back again. Mirror uses local positions so the server and other clients see a massive and unpredictible offset on the object. You also can't use the same parent as the XR rig is a local object and changing parent would just move the object to the other clients hand.

Has anyone managed to create a simple networked object with Mirror and Unity's XR system?

  • \$\begingroup\$ Have you considered making a networked shadow object that invisibly follows the grabbed object locally, and using that as the source object the remote clients see replicated? (With their copy of the true grabbable hidden while it's being held by another player) \$\endgroup\$
    – DMGregory
    Nov 30, 2021 at 3:45
  • \$\begingroup\$ @DMGregory I did try something simular, the issue was that the position wasn't being synced. I have found a temporary solution but it's not elegant - I've created a copy of the XRGrabInteractable script and added a boolean where the parent changes take place. \$\endgroup\$ Nov 30, 2021 at 5:12

1 Answer 1


I have currently created a workaround but to be honest I'm not sure if this will break other aspects of the grab system. I got this working by disabling the reparenting of the object (with a bool) in the Grab() and Drop() methods in the XR Grab Interactable script.

I copied the file XRGrabInteractable.cs from the XR Interaction Toolkit and added a new boolean called changeTransformParent

bool m_ChangeTransformParent = true;

/// <summary>
/// Whether to set the parent of this object when this object is grabbed
/// </summary>
public bool changeTransformParent
    get => m_ChangeTransformParent;
    set => m_ChangeTransformParent = value;

And here are the Grab() and Drop() methods after adding the bool

/// <summary>
/// Updates the state of the object due to being grabbed.
/// Automatically called when entering the Select state.
/// </summary>
/// <seealso cref="Drop"/>
protected void Grab()
    m_OriginalSceneParent = transform.parent;

    if (m_ChangeTransformParent)

    // Special case where the interactor will override this objects movement type (used for Sockets and other absolute interactors)
    m_CurrentMovementType = selectingInteractor.selectedInteractableMovementTypeOverride ?? m_MovementType;


    // Reset detach velocities
    m_DetachVelocity = Vector3.zero;
    m_DetachAngularVelocity = Vector3.zero;

    // Initialize target pose for easing and smoothing
    m_TargetWorldPosition = m_AttachPointCompatibilityMode == AttachPointCompatibilityMode.Default ? transform.position : m_Rigidbody.worldCenterOfMass;
    m_TargetWorldRotation = transform.rotation;
    m_CurrentAttachEaseTime = 0f;



/// <summary>
/// Updates the state of the object due to being dropped and schedule to finish the detach during the end of the frame.
/// Automatically called when exiting the Select state.
/// </summary>
/// <seealso cref="Detach"/>
/// <seealso cref="Grab"/>
protected void Drop()
    if (m_RetainTransformParent && m_OriginalSceneParent != null && !m_OriginalSceneParent.gameObject.activeInHierarchy && m_ChangeTransformParent)
        bool exitingPlayMode;
            // Suppress the warning when exiting Play mode to avoid confusing the user
            exitingPlayMode = UnityEditor.EditorApplication.isPlaying && !UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode;
        exitingPlayMode = false;
        if (!exitingPlayMode)
            Debug.LogWarning("Retain Transform Parent is set to true, and has a non-null Original Scene Parent. " +
                "However, the old parent is deactivated so we are choosing not to re-parent upon dropping.", this);
    else if (m_RetainTransformParent && gameObject.activeInHierarchy && m_ChangeTransformParent)


    m_CurrentMovementType = m_MovementType;
    m_DetachInLateUpdate = true;

Here is the full script - please note this for the specific version: 1.0.0-pre.5

https://pastie.io/rlsfnj.cs link no longer working


Here's a Github repository I'm making with the same scripts https://github.com/richardmuthwill/UnityMirrorXR

TLDR; This is a hack of the original file XRGrabInteractable.cs and might break other things

  • \$\begingroup\$ This is working by just copying the file to anywhere in the Assets folder. If you get any errors you're probably not using 1.0.0-pre.5 If that's the case, just recreate the file using the steps I provided and see the full file for anything else that might help. \$\endgroup\$ Dec 1, 2021 at 10:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .