Grabbing object with realtimetransform doesn't sync position across clients.
I have fruit in my game with an XR Grab Interactable and a RealtimeTransform. When one player picks up a piece of fruit, the others can't see it moving. I'm assuming it has something to do with ownership?
11 Replies
Hi @Alix Ollivier. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
Yes, your issue is almost certainly related to ownership of the RealtimeTransform component.
In Normcore, only the client that owns a RealtimeTransform is considered the "source of truth" for its position, rotation, and other properties. If you want to move an object and have that movement synchronized across all clients, your client must first request ownership of the RealtimeTransform. If you don't do this, any changes you make locally will be overwritten by the values from the current owner (or the initial state if there is no owner), and other clients won't see your updates.
Solution:
When a player grabs a fruit (e.g., via XR Grab Interactable), you should call
When a player grabs a fruit (e.g., via XR Grab Interactable), you should call
RequestOwnership()
on the fruit's RealtimeTransform. This will make the grabbing client the owner, and their movements will be synchronized to all other clients.
Example:
This is a common pattern in Normcore for interactive objects. If you want to check who currently owns the object, you can use the ownerID
property on the RealtimeTransform. Also, make sure you are not requesting ownership of the RealtimeView unless you want to lock down the entire object and its children, as this can prevent proper ownership handoff for interactive objects like grabbable fruit.
For more details, see the documentation on RealtimeTransform and ownership and the networked physics guide.
"In order to move an object with a RealtimeTransform component, your client needs to be the owner of the RealtimeTransform component"If you implement this and still have issues, double-check that you are not reparenting the object at runtime, as this is not supported and can cause synchronization problems. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
RealtimeTransform "When you grab an object, you’ll need to call RequestOwnership() on the RealtimeTransform in order for others to see your updates to that object."
Room Common Questions
<@185325614775926784> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@Normcore Assistant so do I need to make a new script for grabbable objects?
Let me check the knowledge base for you again. One moment! :books:
----------------------
You do not strictly need to make a new script, but you do need to ensure that ownership of the RealtimeTransform is requested when the object is grabbed. This is not handled automatically by Normcore or XR Grab Interactable.
The recommended approach is to add a small script to your grabbable objects that calls
RequestOwnership()
on the RealtimeTransform when the object is picked up. This ensures the grabbing client becomes the owner, and movement is synchronized across all clients. Here’s an example pattern from the documentation:
You can attach this logic to the appropriate grab event in your XR interaction system. This pattern is described in the Normcore docs for interactive objects and is necessary for proper synchronization of movement during grabs RealtimeTransform, Room Common Questions.
So, while you don’t have to make a complex new script, you do need to add this ownership request logic somewhere in your project for grabbable objects.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@185325614775926784> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@Normcore Assistant could you show me where in the docs it shows interactive objects
Let me check the knowledge base for you again. One moment! :books:
----------------------
The Normcore documentation does not have a dedicated section specifically titled "interactive objects," but it does cover the pattern for synchronizing interactive (grabbable) objects using RealtimeTransform and ownership. The most relevant information is found in the following places:
- The RealtimeTransform documentation explains that to move an object and have it sync across clients, your client must own the RealtimeTransform. It states:
"In order to move an object with a RealtimeTransform component, your client needs to be the owner of the RealtimeTransform component. You can request ownership of a RealtimeTransform component by callingThis is the core mechanic for making objects interactive and synchronized. - The Room Common Questions section specifically addresses how to check who is holding an object and emphasizes:RequestOwnership()
or by using the Request Ownership button in the editor inspector."
"When you grab an object, you’ll need to call RequestOwnership()
on the RealtimeTransform in order for others to see your updates to that object."
- The Networked Physics guide also explains the concept of ownership and how it relates to synchronizing movement for objects that are interacted with by multiple clients.
While there isn’t a step-by-step “interactive object” tutorial, these sections together describe the required pattern for making grabbable or interactive objects work correctly in a multiplayer context with Normcore.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@185325614775926784> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@Normcore Assistant does this code look like it would do the trick?
using Normal.Realtime;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class NetworkedGrabbable : MonoBehaviour
{
#region Private Fields
private RealtimeTransform m_RealtimeTransform;
private XRGrabInteractable m_GrabInteractable;
#endregion
#region Unity Lifecycle
private void Awake()
{
// Cache component references
if (!TryGetComponent(out m_RealtimeTransform))
{
Debug.LogError($"[{nameof(NetworkedGrabbable)}] Missing RealtimeTransform component!");
return;
}
if (!TryGetComponent(out m_GrabInteractable))
{
Debug.LogError($"[{nameof(NetworkedGrabbable)}] Missing XRGrabInteractable component!");
return;
}
// Subscribe to grab events
m_GrabInteractable.selectEntered.AddListener(OnGrabbed);
m_GrabInteractable.selectExited.AddListener(OnReleased);
}
private void OnDestroy()
{
// Unsubscribe from events
if (m_GrabInteractable != null)
{
m_GrabInteractable.selectEntered.RemoveListener(OnGrabbed);
m_GrabInteractable.selectExited.RemoveListener(OnReleased);
}
}
#endregion
#region Private Methods
private void OnGrabbed(SelectEnterEventArgs _args)
{
// Request ownership when grabbed
m_RealtimeTransform.RequestOwnership();
}
private void OnReleased(SelectExitEventArgs _args)
{
// Optionally: Clear ownership when released
// m_RealtimeTransform.ClearOwnership();
}
#endregion
}
Let me check the knowledge base for you again. One moment! :books:
----------------------
Your code follows the correct pattern described in the Normcore documentation for synchronizing interactive (grabbable) objects using RealtimeTransform and ownership. Specifically, it requests ownership of the RealtimeTransform when the object is grabbed, which is required for the grabbing client to become the source of truth and have its movements synchronized across all clients:
This matches the recommended approach in the docs:
Your script is aligned with the documented approach for making grabbable objects networked and interactive in Normcore. As long as your objects have both a RealtimeTransform and XRGrabInteractable, this should work as expected. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
"When you grab an object, you’ll need to call RequestOwnership() on the RealtimeTransform in order for others to see your updates to that object."Additionally, your code subscribes and unsubscribes to the grab events in a safe way, and checks for the required components, which is good practice. A few notes based on the docs: - You generally do not need to call
Room Common Questions
ClearOwnership()
on release unless you want to allow other clients to immediately take over. Normcore will often handle this automatically for rigidbodies when they go to sleep, but you can control this behavior as needed (RealtimeTransform + Physics).
- Make sure you are not reparenting the object at runtime, as this is not supported and can cause issues (RealtimeTransform Extra Notes).
Summary:Your script is aligned with the documented approach for making grabbable objects networked and interactive in Normcore. As long as your objects have both a RealtimeTransform and XRGrabInteractable, this should work as expected. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
<@185325614775926784> Kapa.ai is still learning and improving, please let me know how I did by reacting below.