Mikameel
Mikameel2w ago

Design Pattern for Avatar Systems

What is a good design pattern for avatar systems in multiplayer games? I want to start with team colors. Team blue and team Red. I use the default avatars. At first the player doesnt have a team so it is neutral. Should I store the team state as a networked Enum, and then in use the avatar manager to load prefabs for each player depending on the team enum? Or am I missing something.
11 Replies
kapa.ai
kapa.ai2w 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: ---------------------- Based on the knowledge sources provided, I can offer some guidance on implementing a team-based avatar system in NormalVR, though the specific design pattern for team colors isn't directly addressed. Here's a suggested approach using NormalVR's features: 1. Use RealtimeModel for Team State: Create a custom RealtimeModel to store the team state. This could include an enum for the team (Neutral, Blue, Red). 2. Implement with RealtimeComponent: Create a custom RealtimeComponent that uses this model. This component would be responsible for updating the avatar's appearance based on the team state. 3. Avatar Prefab: Use the default avatars as a base, but modify them to include your custom RealtimeComponent. 4. RealtimeAvatarManager: Utilize the RealtimeAvatarManager to manage avatar instantiation. You can customize the localAvatarPrefab property to use your modified prefab that includes the team state component. 5. Update Avatar Appearance: In your custom RealtimeComponent, implement logic to update the avatar's appearance (e.g., materials, meshes) based on the team state in the model. Here's a basic outline of how this might look:
[RealtimeModel]
public partial class TeamStateModel {
[RealtimeProperty(1, true)]
private int _teamID; // 0 for Neutral, 1 for Blue, 2 for Red
}

public class TeamStateComponent : RealtimeComponent<TeamStateModel> {
private void OnTeamChanged(TeamStateModel model, int value) {
UpdateAvatarAppearance(value);
}

private void UpdateAvatarAppearance(int teamID) {
// Logic to change avatar appearance based on team
}
}
[RealtimeModel]
public partial class TeamStateModel {
[RealtimeProperty(1, true)]
private int _teamID; // 0 for Neutral, 1 for Blue, 2 for Red
}

public class TeamStateComponent : RealtimeComponent<TeamStateModel> {
private void OnTeamChanged(TeamStateModel model, int value) {
UpdateAvatarAppearance(value);
}

private void UpdateAvatarAppearance(int teamID) {
// Logic to change avatar appearance based on team
}
}
This approach allows you to network the team state and update the avatar appearance accordingly. The RealtimeAvatarManager will handle the instantiation of avatars, and your custom component will manage the team-specific appearance. Remember to set up ownership and lifetime flags appropriately to control who can change the team state and how it persists in the room. For more detailed information on implementing custom RealtimeComponents and Models, refer to the Synchronizing custom data guide. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP2w ago
what is more efficient in Unity, changing materials of the avatar, or swapping prefabs? @Normcore Assistant
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- While NormalVR doesn't directly address the efficiency comparison between changing materials and swapping prefabs for avatars in Unity, I can provide some general insights based on Unity development practices that may be relevant to your NormalVR project. In Unity, changing materials on an existing avatar is generally more efficient than swapping entire prefabs for the following reasons: 1. Memory usage: Changing materials uses less memory than instantiating new prefabs, as you're only modifying a property of an existing object rather than creating an entirely new one. 2. Performance: Swapping materials is typically faster than destroying and instantiating new prefabs, which can be more resource-intensive. 3. Network traffic: In a networked environment like NormalVR, changing materials would likely require less data to be synced across the network compared to instantiating new prefabs. However, the most efficient approach depends on your specific use case and the complexity of your avatars. If the differences between team avatars involve more than just color changes (e.g., different geometries or components), then prefab swapping might be more appropriate. For NormalVR specifically, you could implement the material-changing approach using a custom RealtimeComponent, as suggested in the Synchronizing custom data guide. This would allow you to efficiently sync the team state and update the avatar's appearance across the network. Remember to profile your specific implementation to ensure it meets your performance requirements in the context of your NormalVR project. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP2w ago
@Normcore Assistant can you change this script to make that I have an event listener that triggers the material change when the value is swapped? using System; using Normal.Realtime; using UnityEngine; namespace Networking.VoteNetworking { public class PlayerTeamSide : RealtimeComponent<TeamSideModel> { [SerializeField] private Material _materialTeamNeutral; [SerializeField] private Material _materialTeamBlue; [SerializeField] private Material _materialTeamRed;
private void OnTeamChanged(TeamSideModel model, int value) { UpdateAvatarAppearance(value); } private void UpdateAvatarAppearance(int teamID) { // Logic to change avatar appearance based on team } } }
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Certainly! I can modify the script to include an event listener that triggers the material change when the team value is changed. Here's the updated script based on the NormalVR documentation:
using System;
using Normal.Realtime;
using UnityEngine;

namespace Networking.VoteNetworking
{
public class PlayerTeamSide : RealtimeComponent<TeamSideModel>
{
[SerializeField] private Material _materialTeamNeutral;
[SerializeField] private Material _materialTeamBlue;
[SerializeField] private Material _materialTeamRed;
[SerializeField] private Renderer _avatarRenderer;

protected override void OnRealtimeModelReplaced(TeamSideModel previousModel, TeamSideModel currentModel)
{
if (previousModel != null)
{
// Unsubscribe from events on the old model.
previousModel.teamIDDidChange -= OnTeamChanged;
}
if (currentModel != null)
{
// Subscribe to events on the new model
currentModel.teamIDDidChange += OnTeamChanged;

// Update the avatar appearance to match the current model
UpdateAvatarAppearance(currentModel.teamID);
}
}

private void OnTeamChanged(TeamSideModel model, int value)
{
UpdateAvatarAppearance(value);
}

private void UpdateAvatarAppearance(int teamID)
{
if (_avatarRenderer == null)
{
Debug.LogError("Avatar Renderer is not assigned!");
return;
}
switch (teamID)
{
case 0:
_avatarRenderer.material = _materialTeamNeutral;
break;
case 1:
_avatarRenderer.material = _materialTeamBlue;
break;
case 2:
_avatarRenderer.material = _materialTeamRed;
break;
default:
Debug.LogWarning("Unknown team ID: " + teamID);
break;
}
}

// Public method to change the team
public void SetTeam(int teamID)
{
model.teamID = teamID;
}
}
}
using System;
using Normal.Realtime;
using UnityEngine;

namespace Networking.VoteNetworking
{
public class PlayerTeamSide : RealtimeComponent<TeamSideModel>
{
[SerializeField] private Material _materialTeamNeutral;
[SerializeField] private Material _materialTeamBlue;
[SerializeField] private Material _materialTeamRed;
[SerializeField] private Renderer _avatarRenderer;

protected override void OnRealtimeModelReplaced(TeamSideModel previousModel, TeamSideModel currentModel)
{
if (previousModel != null)
{
// Unsubscribe from events on the old model.
previousModel.teamIDDidChange -= OnTeamChanged;
}
if (currentModel != null)
{
// Subscribe to events on the new model
currentModel.teamIDDidChange += OnTeamChanged;

// Update the avatar appearance to match the current model
UpdateAvatarAppearance(currentModel.teamID);
}
}

private void OnTeamChanged(TeamSideModel model, int value)
{
UpdateAvatarAppearance(value);
}

private void UpdateAvatarAppearance(int teamID)
{
if (_avatarRenderer == null)
{
Debug.LogError("Avatar Renderer is not assigned!");
return;
}
switch (teamID)
{
case 0:
_avatarRenderer.material = _materialTeamNeutral;
break;
case 1:
_avatarRenderer.material = _materialTeamBlue;
break;
case 2:
_avatarRenderer.material = _materialTeamRed;
break;
default:
Debug.LogWarning("Unknown team ID: " + teamID);
break;
}
}

// Public method to change the team
public void SetTeam(int teamID)
{
model.teamID = teamID;
}
}
}
This updated script includes the following changes: 1. Added an OnRealtimeModelReplaced method to subscribe to the teamIDDidChange event when the model is replaced. This is a key part of the NormalVR RealtimeComponent lifecycle as described in the documentation. 2. The OnTeamChanged method is now used as an event handler for the teamIDDidChange event. 3. Added a _avatarRenderer field to reference the Renderer component of the avatar. 4. Implemented the UpdateAvatarAppearance method to change the material based on the team ID. 5. Added a public SetTeam method to change the team, which will trigger the teamIDDidChange event. To use this script: 1. Make sure your TeamSideModel has a teamID property with the RealtimeProperty attribute. 2. Attach this script to your avatar GameObject. 3. Assign the materials and the avatar's Renderer in the Unity Inspector. 4. Use the SetTeam method to change the team, which will automatically update the avatar's appearance. This implementation follows the RealtimeComponent pattern described in the NormalVR documentation, ensuring that the team changes are properly synchronized across the network. Remember to compile your TeamSideModel after adding the teamID property to generate the necessary networking code. Synchronizing custom data guide ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP2w ago
@Normcore Assistant Okay, just to confirm. If I have 10 avatars, 1 is local. If one avatar changes team side, only the teamstate of this avatar is changed because of what reason? Is it because this realtimecomponent is a child of the Networkedplayer window? please explain clearly as if I am stupid
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Certainly! I'll explain this as clearly as possible, assuming no prior knowledge. You're correct that if one avatar changes team side, only the team state of that specific avatar changes. This behavior is due to how NormalVR structures its networked objects and components. Let's break it down: 1. Individual Avatar Instances: Each avatar in the game, including the local player's avatar, is a separate instance of the player prefab. When you have 10 avatars, you essentially have 10 independent instances of the player object in the scene. 2. RealtimeView and RealtimeComponents: Each avatar instance has its own RealtimeView, which is the parent object that manages the networking for that specific avatar. The RealtimeView contains various RealtimeComponents, including your custom PlayerTeamSide component. 3. Hierarchy and Ownership: The PlayerTeamSide RealtimeComponent is indeed a child of the avatar's RealtimeView. This hierarchical structure is crucial because it determines the scope and ownership of the networked data. 4. Isolated Data: Because each avatar has its own RealtimeView and RealtimeComponents, the data for each avatar (including the team state) is isolated from other avatars. When you change the team state for one avatar, you're only modifying the data for that specific avatar's PlayerTeamSide component. 5. Network Synchronization: When the team state changes for one avatar, NormalVR's networking system only needs to synchronize the data for that specific avatar's PlayerTeamSide component. It doesn't affect or need to sync data for any other avatars. To put it in very simple terms: Imagine each avatar is a separate box. Inside each box is a set of information about that avatar, including its team. When you change the team for one avatar, you're only opening that avatar's box and changing the team information inside it. All the other boxes (avatars) remain untouched and keep their original team information. This design is efficient because: 1. It minimizes network traffic by only sending updates for the changed avatar. 2. It allows for individual control and state management for each avatar. 3. It prevents unintended side effects where changing one avatar might accidentally affect others. RealtimeView documentation provides more details on how this parent-child relationship works in NormalVR's networking system. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w 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?