Mikameel
Mikameel3w ago

Friend Mechanic

I want to create a mechanic to friend people, I'll use my own database for this. What is the best way to approach this? I want to spawn a prefab on the avatar that triggers the friend mechanic, this prefab has it's avatar ID, an visual effect and a collider to trigger friendship connections. Is it better to include this in the player avatar prefab in the hyrarchie and activate it for a specific avatar using a networked event. Or is it better to instantiate a whole new networked prefab object with the desired behaviour? Bit lost on the best way to approach this.
11 Replies
kapa.ai
kapa.ai3w ago
Hi @Mikameel. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- I'll help you implement a friend mechanic in your NormalVR application. Based on the knowledge sources, there are two main approaches you could take: Option 1: Include in Player Avatar Hierarchy You could include the friend mechanic prefab as part of your player avatar hierarchy and activate it when needed. This approach works well because: - RealtimeAvatar components already track and synchronize avatar information across clients - The ownership model ensures only the appropriate client can modify their avatar - It's more efficient for objects that are always associated with an avatar As mentioned in the Avatars documentation, RealtimeAvatarManager keeps track of all avatars in the room and provides events like avatarCreated and avatarDestroyed that you can use to manage friend requests. Option 2: Instantiate Separate Networked Prefabs Alternatively, you could instantiate separate networked prefabs for the friend mechanic: - Use Realtime.Instantiate() to create a prefab that contains your friend request UI/collider - Set ownership appropriately so only the right client can modify it - Connect it to the relevant avatar using the clientID This approach is described in the Realtime API documentation for synchronizing prefabs. Recommendation I recommend Option 1 (including it in the avatar hierarchy) because: 1. It's more efficient - no need to create additional networked objects 2. Ownership is already handled through the avatar hierarchy 3. The friend mechanic is conceptually tied to the avatar You can implement this by: 1. Adding your friend request prefab as a child of your avatar prefab 2. Keeping it disabled by default 3. Creating a custom RealtimeComponent to activate it when needed 4. Using the RealtimeAvatar's clientID to identify which avatar to friend For the actual friend request trigger, you could use a custom RealtimeComponent with a model that tracks friend request state, similar to the approach shown in the Synchronizing Custom Data documentation. If you need RPC-like functionality for the actual friend request event, you could follow the pattern in the RPC Events Recipe, though as noted there, it's generally better to use synchronized state rather than RPC messages when possible. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP3w ago
@Normcore Assistant write me a script that activates a gameobject on specific avatar in the scene.
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'll help you create a script that activates a GameObject on a specific avatar in your NormalVR scene. This is a common requirement when implementing friend mechanics or other avatar-specific interactions. Here's a script that will allow you to activate a GameObject on a specific avatar based on their client ID:
using UnityEngine;
using Normal.Realtime;
public class AvatarObjectActivator : MonoBehaviour
{
[SerializeField] private Realtime _realtime;
[SerializeField] private RealtimeAvatarManager _avatarManager;
[SerializeField] private string _gameObjectName = "FriendRequestObject";

// Call this method to activate an object on a specific avatar
public void ActivateObjectOnAvatar(int targetClientID)
{
// Check if we have a reference to the avatar manager
if (_avatarManager == null)
{
Debug.LogError("Avatar Manager reference is missing!");
return;
}

// Try to find the avatar with the specified client ID
if (_avatarManager.avatars.TryGetValue(targetClientID, out RealtimeAvatar targetAvatar))
{
// Find the GameObject by name in the avatar's hierarchy
Transform objectTransform = targetAvatar.transform.Find(_gameObjectName);

if (objectTransform != null)
{
// Activate the GameObject
objectTransform.gameObject.SetActive(true);
Debug.Log($"Activated {_gameObjectName} on avatar with client ID {targetClientID}");
}
else
{
Debug.LogWarning($"Could not find {_gameObjectName} on avatar with client ID {targetClientID}");
}
}
else
{
Debug.LogWarning($"No avatar found with client ID {targetClientID}");
}
}

// Example: Activate the object on all avatars except the local one
public void ActivateObjectOnAllRemoteAvatars()
{
foreach (var avatarPair in _avatarManager.avatars)
{
// Skip the local avatar
if (avatarPair.Key == _realtime.clientID)
continue;

ActivateObjectOnAvatar(avatarPair.Key);
}
}
}
using UnityEngine;
using Normal.Realtime;
public class AvatarObjectActivator : MonoBehaviour
{
[SerializeField] private Realtime _realtime;
[SerializeField] private RealtimeAvatarManager _avatarManager;
[SerializeField] private string _gameObjectName = "FriendRequestObject";

// Call this method to activate an object on a specific avatar
public void ActivateObjectOnAvatar(int targetClientID)
{
// Check if we have a reference to the avatar manager
if (_avatarManager == null)
{
Debug.LogError("Avatar Manager reference is missing!");
return;
}

// Try to find the avatar with the specified client ID
if (_avatarManager.avatars.TryGetValue(targetClientID, out RealtimeAvatar targetAvatar))
{
// Find the GameObject by name in the avatar's hierarchy
Transform objectTransform = targetAvatar.transform.Find(_gameObjectName);

if (objectTransform != null)
{
// Activate the GameObject
objectTransform.gameObject.SetActive(true);
Debug.Log($"Activated {_gameObjectName} on avatar with client ID {targetClientID}");
}
else
{
Debug.LogWarning($"Could not find {_gameObjectName} on avatar with client ID {targetClientID}");
}
}
else
{
Debug.LogWarning($"No avatar found with client ID {targetClientID}");
}
}

// Example: Activate the object on all avatars except the local one
public void ActivateObjectOnAllRemoteAvatars()
{
foreach (var avatarPair in _avatarManager.avatars)
{
// Skip the local avatar
if (avatarPair.Key == _realtime.clientID)
continue;

ActivateObjectOnAvatar(avatarPair.Key);
}
}
}
To use this script: 1. Add it to a GameObject in your scene (ideally the same one with your Realtime component) 2. Assign the Realtime and RealtimeAvatarManager references in the inspector 3. Make sure your avatar prefab has the GameObject you want to activate (named "FriendRequestObject" by default, but you can change this in the inspector) 4. Call ActivateObjectOnAvatar(clientID) whenever you want to activate the object on a specific avatar For this to work in a networked environment, you'll need to ensure that the activation is synchronized across all clients. You can do this by creating a custom RealtimeComponent that manages the activation state, as described in the Synchronizing Custom Data documentation. Remember that according to the Common Questions documentation, scripts on your player prefab will run for all avatars in the scene, so you'll need to use isOwnedLocallyInHierarchy if you want logic to only apply to certain avatars. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP3w ago
@Normcore Assistant does the client ID changes everytime someone connects to the room or is it consistent accros sessions?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources, the client ID in Normcore is assigned by the server when a client connects to a room, and it is not consistent across sessions. When a client connects to a room, they are assigned a unique client ID for that session. If the same user disconnects and reconnects to the room (or connects in a different session), they will receive a different client ID. This is particularly evident in the offline mode documentation, which states:
Realtime.clientID and Room.clientID will always return 0.
This indicates that in normal online mode, the client ID is dynamically assigned by the server rather than being a persistent identifier tied to a specific user across sessions. If you need to maintain consistent identification of users across sessions for your friend mechanic, you would need to implement your own user identification system using your database, as you mentioned in your initial question. You could store a mapping between your persistent user IDs and the temporary Normcore client IDs during each session. For your friend mechanic, you'll need to consider this when designing how users identify and friend each other, since you can't rely on the Normcore client ID as a persistent identifier. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP3w ago
@Normcore Assistant okay, but like a colorsync model placed on a realtime avatar. Can't I just manage a list of bools that are networked for each avatar? as this list of bools is a child object of the avatar. it will automatically be synced for each avatar right? just like the transform of a avatar
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Yes, you can definitely use a networked list of bools on each avatar, similar to how ColorSync works. This approach makes sense for your friend mechanic. When you place a RealtimeComponent (like ColorSync) on an avatar, it becomes part of the avatar's hierarchy and follows the same ownership rules. As mentioned in the documentation about RealtimeComponents:
A RealtimeComponent subclass keeps a game object in sync with its corresponding RealtimeModel instance in the datastore. When the game object changes, the component updates the model, and when the model changes, the component updates the game object to match.
A RealtimeComponent subclass keeps a game object in sync with its corresponding RealtimeModel instance in the datastore. When the game object changes, the component updates the model, and when the model changes, the component updates the game object to match.
RealtimeComponent documentation Here's why this approach works well: 1. Ownership is already handled: Since the RealtimeAvatar sets ownership of the root RealtimeView to the local client, any components in that hierarchy will follow those ownership rules. As noted in the Common Questions documentation:
"RealtimeAvatar sets the ownership of the root RealtimeView to the local client. This is to ensure that only the local client can modify the avatar."
2. Automatic synchronization: Just like RealtimeTransform synchronizes position data, your custom component can synchronize friendship status booleans. 3. Efficient: This is more efficient than creating separate networked objects. You would need to create a custom RealtimeComponent with a RealtimeModel that contains your list of booleans (or perhaps a RealtimeSet or RealtimeDictionary depending on your exact needs). Each avatar would have this component, and it would track which other avatars are "friended." Just remember that since client IDs aren't persistent across sessions (as we discussed), you'll need your own user identification system in your database to maintain friendship relationships between sessions. This approach is very similar to the ColorSync example from the Synchronizing Custom Data documentation, but instead of syncing a color, you'd be syncing friendship status. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?