fgamez1020
fgamez10202w ago

Avatar sync

I have a 4 player game in VR, when an avatar syncs to other players I want to place them in differente positions depending where all the players are, I have a table and I want to place them on each side of that table.
5 Replies
kapa.ai
kapa.ai2w ago
Hi @fgamez1020. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To implement avatar synchronization with custom positioning in your 4-player VR game using NormalVR, you can utilize the RealtimeAvatarManager component along with some custom logic. Here's an approach you can take: 1. First, set up your avatar system using the RealtimeAvatarManager as described in the Avatars documentation (https://normcore.io/documentation/realtime/xr/avatars#realtimeavatarmanager). 2. Create a script to manage the positioning of avatars around your table. This script should: - Keep track of available positions around the table - Assign positions to avatars as they join - Update positions if players leave 3. Use the avatarCreated event from RealtimeAvatarManager to detect when new avatars join and position them accordingly. Here's a basic example of how you might implement this:
using UnityEngine;
using Normal.Realtime;

public class AvatarPositioner : MonoBehaviour
{
public Transform[] tablePositions;
private RealtimeAvatarManager _avatarManager;
private Dictionary<int, int> _avatarPositions = new Dictionary<int, int>();

private void Awake()
{
_avatarManager = GetComponent<RealtimeAvatarManager>();
_avatarManager.avatarCreated += OnAvatarCreated;
_avatarManager.avatarDestroyed += OnAvatarDestroyed;
}

private void OnAvatarCreated(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
int positionIndex = GetNextAvailablePosition();
if (positionIndex != -1)
{
avatar.transform.position = tablePositions[positionIndex].position;
avatar.transform.rotation = tablePositions[positionIndex].rotation;
_avatarPositions[avatar.ownerID] = positionIndex;
}
}
private void OnAvatarDestroyed(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (_avatarPositions.ContainsKey(avatar.ownerID))
{
_avatarPositions.Remove(avatar.ownerID);
}
}

private int GetNextAvailablePosition()
{
for (int i = 0; i < tablePositions.Length; i++)
{
if (!_avatarPositions.ContainsValue(i))
{
return i;
}
}
return -1; // No available positions
}
}
using UnityEngine;
using Normal.Realtime;

public class AvatarPositioner : MonoBehaviour
{
public Transform[] tablePositions;
private RealtimeAvatarManager _avatarManager;
private Dictionary<int, int> _avatarPositions = new Dictionary<int, int>();

private void Awake()
{
_avatarManager = GetComponent<RealtimeAvatarManager>();
_avatarManager.avatarCreated += OnAvatarCreated;
_avatarManager.avatarDestroyed += OnAvatarDestroyed;
}

private void OnAvatarCreated(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
int positionIndex = GetNextAvailablePosition();
if (positionIndex != -1)
{
avatar.transform.position = tablePositions[positionIndex].position;
avatar.transform.rotation = tablePositions[positionIndex].rotation;
_avatarPositions[avatar.ownerID] = positionIndex;
}
}
private void OnAvatarDestroyed(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (_avatarPositions.ContainsKey(avatar.ownerID))
{
_avatarPositions.Remove(avatar.ownerID);
}
}

private int GetNextAvailablePosition()
{
for (int i = 0; i < tablePositions.Length; i++)
{
if (!_avatarPositions.ContainsValue(i))
{
return i;
}
}
return -1; // No available positions
}
}
This script assumes you have an array of Transform objects representing the positions around your table. It assigns each new avatar to an available position and keeps track of which positions are occupied. Remember to add this script to the same GameObject as your RealtimeAvatarManager and set up the tablePositions array in the Inspector. Note that this is a basic implementation and you might need to adjust it based on your specific requirements, such as handling cases where more than 4 players try to join or implementing more sophisticated positioning logic. For more advanced avatar customization, you can refer to the Modifying RealtimeAvatar scripts (https://normcore.io/documentation/realtime/xr/avatars#modifying-realtimeavatar-scripts) section of the documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1053407831916822528> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
fgamez1020
fgamez1020OP2w ago
but for example, my avatar should show where I am, but my synced avatar should show to the other players in a different position to where they are @kapa.ai but for example, my avatar should show where I am, but my synced avatar should show to the other players in a different position to where they are
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I understand you want to display your avatar in different positions for yourself and other players. This is an interesting scenario that requires some custom logic on top of Normcore's avatar system. Let's break this down: 1. For your local view, you want your avatar to represent your actual position in the VR space. 2. For other players, you want your avatar to appear at a predetermined position (e.g., around a table). To achieve this, we can modify the approach using Normcore's RealtimeAvatar component. Here's how you might implement this: 1. First, set up your avatar prefab with a RealtimeAvatar component as described in the RealtimeAvatar documentation (https://normcore.io/documentation/realtime/xr/avatars#realtimeavatar). 2. Create a custom script to manage the avatar positioning. This script should: - Keep the local avatar at the player's actual position - Position remote avatars at predetermined locations Here's a basic example of how you might implement this:
using UnityEngine;
using Normal.Realtime;

public class CustomAvatarPositioner : MonoBehaviour
{
public Transform[] tablePositions;
private RealtimeAvatarManager _avatarManager;
private Dictionary<int, RealtimeAvatar> _remoteAvatars = new Dictionary<int, RealtimeAvatar>();

private void Awake()
{
_avatarManager = GetComponent<RealtimeAvatarManager>();
_avatarManager.avatarCreated += OnAvatarCreated;
_avatarManager.avatarDestroyed += OnAvatarDestroyed;
}
private void OnAvatarCreated(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (!isLocalAvatar)
{
int positionIndex = GetNextAvailablePosition();
if (positionIndex != -1)
{
avatar.transform.position = tablePositions[positionIndex].position;
avatar.transform.rotation = tablePositions[positionIndex].rotation;
_remoteAvatars[avatar.ownerID] = avatar;
}
}
}

private void OnAvatarDestroyed(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (!isLocalAvatar && _remoteAvatars.ContainsKey(avatar.ownerID))
{
_remoteAvatars.Remove(avatar.ownerID);
}
}

private int GetNextAvailablePosition()
{
for (int i = 0; i < tablePositions.Length; i++)
{
if (!_remoteAvatars.Values.Any(avatar => avatar.transform.position == tablePositions[i].position))
{
return i;
}
}
return -1; // No available positions
}

private void Update()
{
// Keep remote avatars at their assigned positions
foreach (var avatar in _remoteAvatars.Values)
{
int positionIndex = System.Array.FindIndex(tablePositions, t => t.position == avatar.transform.position);
if (positionIndex != -1)
{
avatar.transform.position = tablePositions[positionIndex].position;
avatar.transform.rotation = tablePositions[positionIndex].rotation;
}
}
}
}
using UnityEngine;
using Normal.Realtime;

public class CustomAvatarPositioner : MonoBehaviour
{
public Transform[] tablePositions;
private RealtimeAvatarManager _avatarManager;
private Dictionary<int, RealtimeAvatar> _remoteAvatars = new Dictionary<int, RealtimeAvatar>();

private void Awake()
{
_avatarManager = GetComponent<RealtimeAvatarManager>();
_avatarManager.avatarCreated += OnAvatarCreated;
_avatarManager.avatarDestroyed += OnAvatarDestroyed;
}
private void OnAvatarCreated(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (!isLocalAvatar)
{
int positionIndex = GetNextAvailablePosition();
if (positionIndex != -1)
{
avatar.transform.position = tablePositions[positionIndex].position;
avatar.transform.rotation = tablePositions[positionIndex].rotation;
_remoteAvatars[avatar.ownerID] = avatar;
}
}
}

private void OnAvatarDestroyed(RealtimeAvatarManager avatarManager, RealtimeAvatar avatar, bool isLocalAvatar)
{
if (!isLocalAvatar && _remoteAvatars.ContainsKey(avatar.ownerID))
{
_remoteAvatars.Remove(avatar.ownerID);
}
}

private int GetNextAvailablePosition()
{
for (int i = 0; i < tablePositions.Length; i++)
{
if (!_remoteAvatars.Values.Any(avatar => avatar.transform.position == tablePositions[i].position))
{
return i;
}
}
return -1; // No available positions
}

private void Update()
{
// Keep remote avatars at their assigned positions
foreach (var avatar in _remoteAvatars.Values)
{
int positionIndex = System.Array.FindIndex(tablePositions, t => t.position == avatar.transform.position);
if (positionIndex != -1)
{
avatar.transform.position = tablePositions[positionIndex].position;
avatar.transform.rotation = tablePositions[positionIndex].rotation;
}
}
}
}
This script does the following: 1. It only positions remote avatars (non-local) at predetermined table positions. 2. The local avatar is left untouched, so it will represent the player's actual position in VR space. 3. Remote avatars are kept at their assigned positions in the Update method to ensure they don't move from their predetermined spots. To use this script: 1. Attach it to the same GameObject as your RealtimeAvatarManager. 2. Set up the tablePositions array in the Inspector with Transform objects representing the positions around your table. Remember, this is a basic implementation. You might need to adjust it based on your specific requirements, such as handling cases where more players join than available positions, or implementing more sophisticated positioning logic. For more advanced avatar customization, you can refer to the Modifying RealtimeAvatar scripts (https://normcore.io/documentation/realtime/xr/avatars#modifying-realtimeavatar-scripts) section of the Normcore documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
Mechabit
Mechabit2w ago
if you want to fake your position I wouldn't use RealtimeTransform and just read how many players are in the scene locally and place avatar dummies where ever you want or you can put the remote avatars as children of the seats around the table RealtimeTransform doesn't support reparenting out of the box so you'll have to sort out a custom solution