Search Unity

Question NetworkVariable in Netcode for game objects not working

Discussion in 'Netcode for GameObjects' started by abc4c, Nov 9, 2021.

  1. abc4c

    abc4c

    Joined:
    Jan 12, 2021
    Posts:
    1
    Hi , I am new to Netcode for game objects.

    Recently, I tested NetworkVariable functionality in both MLAPI 0.1.0 and Netcode 1.0.0. In my project, I tried to create a scene "Start Point" for handling server connection. Then, I created a Lobby scene to manage the names of connected players.

    In StartPoint Scene, input field will be provided to enter text. In Lobby scene, the TextUI will be update when NetworkVariable is changed, and clients who are joining server will modify NetworkVariable<FixedString64Bytes> by their text in StartPoint scene .

    The problem is, when using Netcode 1.0.0, as mentioned in title, NetworkVariables from clients/ host does not sync together. My current solution is, I need to call ServerRpc--> ClientRpc to manage variable changes.

    Here is my setting from inspector:
    螢幕截圖 2021-11-09 下午10.20.21.png
    Here is my code:
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Netcode;
    3. using UnityEngine.SceneManagement;
    4. using Unity.Collections;
    5. using TMPro;
    6.  
    7. public class StartPoint : NetworkBehaviour
    8. {
    9.     public TMP_InputField YourName_Field;
    10.  
    11.     [SerializeField]
    12.     public static string YourName;
    13.  
    14.     // a variable belongs to one client and can be read by all clients
    15.     public static NetworkVariable<FixedString64Bytes> PlayerName = new NetworkVariable<FixedString64Bytes>();    
    16.  
    17.     public static StartPoint singleton = null;
    18.  
    19.     public GameObject SetActivePanel;
    20.  
    21.     public void Awake()
    22.     {
    23.         DontDestroyOnLoad(this.gameObject);
    24.         if(singleton == null)
    25.         {
    26.             singleton = this;
    27.         }
    28.         else if (singleton != this)
    29.         {
    30.             Debug.Log("destroy");
    31.             Destroy(this);
    32.         }
    33.  
    34.     }
    35.  
    36.     public void StartHost()
    37.     {
    38.         NetworkManager.Singleton.StartHost();
    39.         InitPlayerName();
    40.         NetworkManager.Singleton.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Single);
    41.         Debug.Log("starthost");
    42.         SetActivePanel.SetActive(false);
    43.         NetworkManager.Singleton.SceneManager.LoadScene("Lobby" , LoadSceneMode.Single);
    44.     }
    45.  
    46.  
    47.     public void StartClient()
    48.     {
    49.         NetworkManager.Singleton.StartClient();
    50.         InitPlayerName();
    51.         Debug.Log("Start Client");
    52.         SetActivePanel.SetActive(false);
    53.         NetworkManager.Singleton.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Single);
    54.  
    55.     }
    56.  
    57.     [ServerRpc(RequireOwnership =false)]
    58.     public void WriteStringServerRpc(string name)          
    59.     {
    60.         PlayerName.Value = name;
    61.         PrintNetworkVariableValueClientRpc();
    62.         //WriteStringClientRpc(name);                          
    63.     }
    64.     //testing function
    65.     [ClientRpc]
    66.     public void PrintNetworkVariableValueClientRpc()
    67.     {
    68.         Debug.Log(PlayerName.Value);
    69.     }
    70.  
    71.     [ClientRpc]
    72.     public void WriteStringClientRpc(string name)
    73.     {
    74.         PlayerName.Value = name;
    75.     }
    76.  
    77.     // set your name
    78.     public void InitPlayerName()
    79.     {
    80.         YourName = YourName_Field.text;
    81.     }
    82. }
    Code (CSharp):
    1. public class LobbyStart : NetworkBehaviour
    2. {
    3.     // the text that display player name
    4.     [SerializeField]
    5.     public TextMeshProUGUI PlayerText;
    6.  
    7.     int i = 1;
    8.     private void Awake()
    9.     {
    10.         PlayerText = GameObject.Find("PlayerName").GetComponent<TextMeshProUGUI>();
    11.     }
    12.     public void Start2()
    13.     {
    14.         StartPoint.PlayerName.OnValueChanged += HandleNameChange;           // listener
    15.         StartPoint.singleton.WriteStringServerRpc(StartPoint.YourName);
    16.         Debug.Log("Current PlayerName is : " + StartPoint.PlayerName.Value);
    17.     }
    18.     public override void OnNetworkSpawn()
    19.     {
    20.         StartPoint.PlayerName.OnValueChanged += HandleNameChange;           // listener
    21.         StartPoint.singleton.WriteStringServerRpc(StartPoint.YourName);
    22.         Debug.Log("Current PlayerName is : " + StartPoint.PlayerName.Value);
    23.     }
    24.     public override void OnDestroy()
    25.     {
    26.         Debug.Log("OnDestroy");
    27.         StartPoint.PlayerName.OnValueChanged -= HandleNameChange;
    28.     }
    29.  
    30.     public void ChangeNetworkVariableButton()
    31.     {
    32.         StartPoint.singleton.WriteStringServerRpc("test" + i.ToString());         //called by client , excuted on host/server
    33.         i++;
    34.     }
    35.  
    36.     private void HandleNameChange(FixedString64Bytes previousValue, FixedString64Bytes newValue)
    37.     {
    38.        PlayerText.text = newValue.ToString();
    39.         Debug.Log("HandleName Change , " + " The Name is :" + newValue);
    40.  
    41.     }
    42. }
    Anything missing?
     
  2. keksuyt

    keksuyt

    Joined:
    Dec 10, 2021
    Posts:
    2
    For me I'm using UnityID's and I made "PlayerHUD" script and attached it to player with gameobject "PlayerInfo" as children and that gameobject had textmeshpro component.

    upload_2021-12-15_9-22-17.png

    I'm not sure how to fix you're code but this was my solution for usernames.

    Code (CSharp):
    1.  
    2. using TMPro;
    3. using Unity.Netcode;
    4. using UnityEngine;
    5. using UnityEngine.SceneManagement;
    6. using Unity.Collections;
    7. using System.Threading.Tasks;
    8. using System.Collections.Generic;
    9. using Unity.Services.Authentication;
    10. using Unity.Services.CloudSave;
    11. using Unity.Services.Core;
    12. using Unity.Services.Core.Environments;
    13.  
    14. public class PlayerHud : NetworkBehaviour
    15. {
    16.   [SerializeField]
    17.   private NetworkVariable<NetworkString> playerNetworkName = new NetworkVariable<NetworkString>();
    18.  
    19.   [SerializeField]
    20.   private bool overlaySet = false;
    21.  
    22.   [SerializeField]
    23.   private string PlayerName;
    24.  
    25.   public async override void OnNetworkSpawn()
    26.   {
    27.     Dictionary<string, string> usernameData = await SaveData.LoadAsync(new HashSet<string> {"username"});
    28.     PlayerName = usernameData["username"];
    29.     Debug.LogWarning($"Got username: {PlayerName}");
    30.  
    31.     if(!string.IsNullOrEmpty(PlayerName))
    32.     {
    33.         SetNameServerRpc(PlayerName);
    34.         Debug.LogWarning($"Sending RPC");
    35.     }
    36.       //if(IsServer)
    37.       //{
    38.         //  playerNetworkName.Value = $"Player {PlayerName}";
    39.       //    Debug.LogWarning($"Username Check as Server: {PlayerName}");
    40.       //}
    41.  
    42.   }
    43.  
    44.   public void SetOverlay()
    45.   {
    46.       var localPlayerOverlay = gameObject.GetComponentInChildren<TextMeshPro>();
    47.       localPlayerOverlay.text = $"{playerNetworkName.Value}";
    48.   }
    49.  
    50.   public void Update()
    51.   {
    52.       if(!overlaySet && !string.IsNullOrEmpty(playerNetworkName.Value))
    53.       {
    54.           SetOverlay();
    55.           overlaySet = true;
    56.       }
    57.   }
    58.  
    59.   [ServerRpc/*(RequireOwnership = false)*/]
    60.   public void SetNameServerRpc(string UserName)
    61.   {
    62.     Debug.LogWarning($"RPC reached");
    63.     Debug.LogWarning($"Username Check as Server: {UserName}");
    64.     playerNetworkName.Value = $"{UserName}";
    65.     Debug.LogWarning($"Changed NetworkVariable to {playerNetworkName.Value}");
    66.   }
    67. }
    68.  
    EDIT:
    Also I forgot about NetworkString.cs which I imported Assets/Scripts/Shared

    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Netcode;
    3.  
    4. public struct NetworkString : INetworkSerializable
    5. {
    6.     private FixedString32Bytes info;
    7.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    8.     {
    9.         serializer.SerializeValue(ref info);
    10.     }
    11.  
    12.     public override string ToString()
    13.     {
    14.         return info.ToString();
    15.     }
    16.  
    17.     public static implicit operator string(NetworkString s) => s.ToString();
    18.     public static implicit operator NetworkString(string s) => new NetworkString() { info = new FixedString32Bytes(s) };
    19. }
    20.  
    EDIT: (Again I'm sry)

    But I fixed in that code the "" marks in names so I'ma just drop the new code here
    Code (CSharp):
    1. using TMPro;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5. using Unity.Collections;
    6. using System.Threading.Tasks;
    7. using System.Collections.Generic;
    8. using Unity.Services.Authentication;
    9. using Unity.Services.CloudSave;
    10. using Unity.Services.Core;
    11. using Unity.Services.Core.Environments;
    12. using System;
    13.  
    14. public class PlayerHud : NetworkBehaviour
    15. {
    16.   [SerializeField]
    17.   private NetworkVariable<NetworkString> playerNetworkName = new NetworkVariable<NetworkString>();
    18.  
    19.   [SerializeField]
    20.   private bool overlaySet = false;
    21.  
    22.   [SerializeField]
    23.   private string PlayerName;
    24.  
    25.   [SerializeField]
    26.   private string playerName;
    27.  
    28.   public async override void OnNetworkSpawn()
    29.   {
    30.     Dictionary<string, string> usernameData = await SaveData.LoadAsync(new HashSet<string> {"username"});
    31.     PlayerName = usernameData["username"];
    32.     playerName = PlayerName.Trim( new Char[] {'"', '.' } );
    33.     Debug.LogWarning($"Got username: {playerName}");
    34.  
    35.     if(!string.IsNullOrEmpty(playerName))
    36.     {
    37.         SetNameServerRpc(playerName);
    38.         Debug.LogWarning($"Sending RPC");
    39.     }
    40.       //if(IsServer)
    41.       //{
    42.         //  playerNetworkName.Value = $"Player {PlayerName}";
    43.       //    Debug.LogWarning($"Username Check as Server: {PlayerName}");
    44.       //}
    45.  
    46.   }
    47.  
    48.   public void SetOverlay()
    49.   {
    50.       var localPlayerOverlay = gameObject.GetComponentInChildren<TextMeshPro>();
    51.       localPlayerOverlay.text = $"{playerNetworkName.Value}";
    52.   }
    53.  
    54.   public void Update()
    55.   {
    56.       if(!overlaySet && !string.IsNullOrEmpty(playerNetworkName.Value))
    57.       {
    58.           SetOverlay();
    59.           overlaySet = true;
    60.       }
    61.   }
    62.  
    63.   [ServerRpc/*(RequireOwnership = false)*/]
    64.   public void SetNameServerRpc(string UserName)
    65.   {
    66.     Debug.LogWarning($"RPC reached");
    67.     Debug.LogWarning($"Username Check as Server: {UserName}");
    68.     playerNetworkName.Value = $"{UserName}";
    69.     Debug.LogWarning($"Changed NetworkVariable to {playerNetworkName.Value}");
    70.   }
    71. }
    72.  
     
    Last edited: Dec 15, 2021
  3. happydog2020

    happydog2020

    Joined:
    Aug 20, 2021
    Posts:
    1
    1. private FixedString32Bytes info;
    2. public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    3. {
    4. serializer.SerializeValue(ref info);
     
  4. AaronKB

    AaronKB

    Joined:
    Aug 16, 2019
    Posts:
    27
    I've had a similar issue, and implementing NetworkSerialize for FixedString32Bytes doesn't work because it complains about

    error CS0315: The type 'Unity.Collections.FixedString64Bytes' cannot be used as type parameter 'T' in the generic type or method 'BufferSerializer<T>.SerializeValue<T>(ref T, FastBufferWriter.ForPrimitives)'. There is no boxing conversion from 'Unity.Collections.FixedString64Bytes' to 'System.IComparable'.
     
  5. WallaceHS

    WallaceHS

    Joined:
    Aug 7, 2021
    Posts:
    9
    My compiler tells me that "The type 'Unity.Collections.FixedString32Bytes' cannot be used as type parameter 'T' in the generic type or method 'BufferSerializer<T>.SerializeValue<T>(ref T, FastBufferWriter.ForPrimitives)'. There is no boxing conversion from 'Unity.Collections.FixedString32Bytes' to 'System.IComparable'."
     
  6. luke-unity

    luke-unity

    Joined:
    Sep 30, 2020
    Posts:
    306
    Starting from pre.8 you have to use
    NetworkVariable<ForceNetworkSerializeByMemcpy<FixedString32Bytes>>
    instead
     
  7. KadaXuanwu

    KadaXuanwu

    Joined:
    Oct 26, 2019
    Posts:
    9
    Hey, in pre.7 I used
    Code (CSharp):
    1. _name.Value = (FixedString64Bytes) playerNameInput.text;
    to write a string to the network variable FixedString64Bytes.
    Now with
    Code (CSharp):
    1. NetworkVariable<ForceNetworkSerializeByMemcpy<FixedString32Bytes>>
    it doesn't work anymore. (Cannot convert type 'string' to 'Unity.Netcode.ForceNetworkSerializeByMemcpy<FixedString32Bytes>')

    How can you write or read a string from this type?
     
    stancanepa likes this.
  8. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    Writing that way should still work.

    For writing:
    Code (CSharp):
    1. _name.Value = new FixedString64Bytes("myString");
    2.   // or
    3. _name.Value = (FixedString64Bytes) "myString";
    For reading:
    Code (CSharp):
    1. string name = _name.Value.Value.ToString();
    2. // or
    3. string name = ((FixedString64Bytes)_name.Value).ToString();
     
    stancanepa and KadaXuanwu like this.