Harper
Harper2mo ago

Is it possible to set the RealtimeAvatarManager local player at runtime in script?

I instantiate my player at runtime because I have desktop and VR clients which each have their own distinct player controllers. When a VR client loads a scene, I would like to manually set the avatar manager's root/head/hand transforms. Previously, I would have a desktop client and VR client in the scene before the game starts and would remove the unnecessary one. But, spawning the needed one is cleaner and leads to less issues (or so one would think).
11 Replies
kapa.ai
kapa.ai2mo ago
Hi @Harper. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Yes, it is possible to set the RealtimeAvatarManager's local player transforms at runtime in script. The RealtimeAvatarManager component allows you to dynamically set the root, head, and hand transforms for the local avatar. Based on the information from the XR Avatars and Voice Chat guide, you can modify the RealtimeAvatarManager component at runtime to set the local player transforms. Here's how you can approach this: 1. After instantiating your VR or desktop player controller at runtime, you'll need to get a reference to the RealtimeAvatarManager in your scene. 2. Then, you can set the root, head, and hand transforms of the RealtimeAvatarManager to match your instantiated player controller. Here's a basic example of how you might do this in script:
using Normal.Realtime;
using UnityEngine;

public class PlayerSetup : MonoBehaviour
{
public GameObject vrPlayerPrefab;
public GameObject desktopPlayerPrefab;

private void Start()
{
GameObject playerInstance;

// Instantiate the appropriate player prefab
if (/* VR mode check */)
{
playerInstance = Instantiate(vrPlayerPrefab);
}
else
{
playerInstance = Instantiate(desktopPlayerPrefab);
}

// Get the RealtimeAvatarManager component
RealtimeAvatarManager avatarManager = FindObjectOfType<RealtimeAvatarManager>();
if (avatarManager != null)
{
// Set the transforms based on your instantiated player
avatarManager.localPlayerRootTransform = playerInstance.transform;
avatarManager.localPlayerHeadTransform = playerInstance.transform.Find("Head");
avatarManager.localPlayerLeftHandTransform = playerInstance.transform.Find("LeftHand");
avatarManager.localPlayerRightHandTransform = playerInstance.transform.Find("RightHand");
}
}
}
using Normal.Realtime;
using UnityEngine;

public class PlayerSetup : MonoBehaviour
{
public GameObject vrPlayerPrefab;
public GameObject desktopPlayerPrefab;

private void Start()
{
GameObject playerInstance;

// Instantiate the appropriate player prefab
if (/* VR mode check */)
{
playerInstance = Instantiate(vrPlayerPrefab);
}
else
{
playerInstance = Instantiate(desktopPlayerPrefab);
}

// Get the RealtimeAvatarManager component
RealtimeAvatarManager avatarManager = FindObjectOfType<RealtimeAvatarManager>();
if (avatarManager != null)
{
// Set the transforms based on your instantiated player
avatarManager.localPlayerRootTransform = playerInstance.transform;
avatarManager.localPlayerHeadTransform = playerInstance.transform.Find("Head");
avatarManager.localPlayerLeftHandTransform = playerInstance.transform.Find("LeftHand");
avatarManager.localPlayerRightHandTransform = playerInstance.transform.Find("RightHand");
}
}
}
This approach allows you to dynamically set up the RealtimeAvatarManager based on your runtime-instantiated player controller, whether it's for VR or desktop. Remember to adjust the transform paths (e.g., "Head", "LeftHand", "RightHand") to match the actual hierarchy of your player prefabs. By setting these transforms at runtime, you ensure that the RealtimeAvatarManager correctly represents your instantiated player, allowing for proper avatar synchronization in your multiplayer NormalVR application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@276154194606424064> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Harper
HarperOP2mo ago
@Normcore Assistant RealtimeAvatarManager does not have those transforms. Looks like I may be able to set it from RealtimeAvatarManager.localAvatar.localPlayer though.
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I apologize for the confusion in my previous response. You are correct, and thank you for pointing that out. The RealtimeAvatarManager indeed does not have direct transform properties as I incorrectly suggested. Let's look at the correct way to set this up using the localAvatar.localPlayer as you mentioned. According to the XR Avatars and Voice Chat guide, the RealtimeAvatarManager is responsible for instantiating and managing avatars. The local player's transforms are actually set through the RealtimeAvatar component, which is instantiated by the RealtimeAvatarManager. Here's how you can correctly set the transforms at runtime: 1. First, instantiate your player (VR or desktop) as before. 2. Then, you need to wait for the RealtimeAvatarManager to instantiate the local avatar. 3. Once the local avatar is instantiated, you can set the transforms on the localPlayer property. Here's an example of how you might implement this:
using Normal.Realtime;
using UnityEngine;

public class PlayerSetup : MonoBehaviour
{
public GameObject vrPlayerPrefab;
public GameObject desktopPlayerPrefab;
private RealtimeAvatarManager avatarManager;

private void Start()
{
avatarManager = FindObjectOfType<RealtimeAvatarManager>();
if (avatarManager != null)
{
avatarManager.avatarCreated += OnAvatarCreated;
}

GameObject playerInstance = InstantiatePlayer();
// Store playerInstance or its transforms for later use
}

private GameObject InstantiatePlayer()
{
if (/* VR mode check */)
{
return Instantiate(vrPlayerPrefab);
}
else
{
return Instantiate(desktopPlayerPrefab);
}
}
private void OnAvatarCreated(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (isLocalAvatar)
{
SetLocalPlayerTransforms(avatar);
}
}

private void SetLocalPlayerTransforms(RealtimeAvatar avatar)
{
// Assuming you've stored a reference to your instantiated player or its transforms
avatar.localPlayer.root = /* Your instantiated player's root transform */;
avatar.localPlayer.head = /* Your instantiated player's head transform */;
avatar.localPlayer.leftHand = /* Your instantiated player's left hand transform */;
avatar.localPlayer.rightHand = /* Your instantiated player's right hand transform */;
}
}
using Normal.Realtime;
using UnityEngine;

public class PlayerSetup : MonoBehaviour
{
public GameObject vrPlayerPrefab;
public GameObject desktopPlayerPrefab;
private RealtimeAvatarManager avatarManager;

private void Start()
{
avatarManager = FindObjectOfType<RealtimeAvatarManager>();
if (avatarManager != null)
{
avatarManager.avatarCreated += OnAvatarCreated;
}

GameObject playerInstance = InstantiatePlayer();
// Store playerInstance or its transforms for later use
}

private GameObject InstantiatePlayer()
{
if (/* VR mode check */)
{
return Instantiate(vrPlayerPrefab);
}
else
{
return Instantiate(desktopPlayerPrefab);
}
}
private void OnAvatarCreated(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (isLocalAvatar)
{
SetLocalPlayerTransforms(avatar);
}
}

private void SetLocalPlayerTransforms(RealtimeAvatar avatar)
{
// Assuming you've stored a reference to your instantiated player or its transforms
avatar.localPlayer.root = /* Your instantiated player's root transform */;
avatar.localPlayer.head = /* Your instantiated player's head transform */;
avatar.localPlayer.leftHand = /* Your instantiated player's left hand transform */;
avatar.localPlayer.rightHand = /* Your instantiated player's right hand transform */;
}
}
This approach uses the avatarCreated event of the RealtimeAvatarManager to detect when the local avatar is instantiated. Once it's created, you can set the transforms on the localPlayer property of the RealtimeAvatar component. Remember to adjust the transform assignments in the SetLocalPlayerTransforms method to match your actual player prefab structure. This method should allow you to correctly set up your avatar transforms at runtime, whether you're using a VR or desktop client. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@276154194606424064> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Harper
HarperOP2mo ago
I also need to keep it from spawning an avatar when there is a desktop client Ended up doing this. Would love to know if there's a better way.
// The only way to set the avatar manually is to wait
private void SetLocalPlayer(Realtime realtime, RealtimeAvatarManager avatarManager)
{
IEnumerator Wait()
{
while (!realtime.connected) yield return null;
yield return new WaitForSeconds(0.1f);
avatarManager.localAvatar.localPlayer.root = rootTransform;
avatarManager.localAvatar.localPlayer.head = CameraTransform;
avatarManager.localAvatar.localPlayer.leftHand = leftHandTransform;
avatarManager.localAvatar.localPlayer.rightHand = rightHandTransform;
}

StartCoroutine(Wait());
}
// The only way to set the avatar manually is to wait
private void SetLocalPlayer(Realtime realtime, RealtimeAvatarManager avatarManager)
{
IEnumerator Wait()
{
while (!realtime.connected) yield return null;
yield return new WaitForSeconds(0.1f);
avatarManager.localAvatar.localPlayer.root = rootTransform;
avatarManager.localAvatar.localPlayer.head = CameraTransform;
avatarManager.localAvatar.localPlayer.leftHand = leftHandTransform;
avatarManager.localAvatar.localPlayer.rightHand = rightHandTransform;
}

StartCoroutine(Wait());
}
maxweisel
maxweisel2mo ago
We don’t include a way to set it but the source code is included. I’d fork it and update so you can set it after Start() (and also set it to provide the new values to the local avatar instance)
Harper
HarperOP2mo ago
I'm happy enough with the workaround for now. My team has built up a policy against modifying asset files due to the slippery-ness of that slope. Better to peek inside the black box and work around its quirks rather than modify the black box and create more quirks that cost more time to solve. If I open up a feature request for support of desktop users in an avatar system (or some other suggested solutions) would that be worth it or would it simply be recommended I create a custom avatar manager that supports both?
Mechabit
Mechabit2mo ago
make a custom avatar manager, it's not hard to support multiple platforms
Harper
HarperOP2mo ago
Sure... I'm just in deep now so its simpler said than done, but I think that is the solution
Mechabit
Mechabit2mo ago
just copy the contents of the basic avatar manager and have 2 prefabs for avatar to spawn based on the platform

Did you find this page helpful?