Search Unity

Question Network objects with INetworkSerializable fields

Discussion in 'Netcode for GameObjects' started by cerestorm, Nov 22, 2021.

  1. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    I'm not able to get any INetworkSerializable fields in a network object to be serialized.

    Here's a simple example:
    Code (CSharp):
    1. public class Side : NetworkBehaviour
    2. {
    3.     public City city;
    4. }
    5.  
    6.  
    7. public class City : INetworkSerializable
    8. {
    9.     public uint cityId = 100;
    10.  
    11.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    12.     {
    13.         serializer.SerializeValue(ref cityId);
    14.     }
    15. }
    16.  
    17.  
    18.     private void SideSpawn()
    19.     {
    20.         Side side = SpawnService.Instance().InstantiatePrefab<Side>();
    21.         side.city = new City();
    22.  
    23.         side.GetComponent<NetworkObject>().Spawn();
    24.     }
    City is always null on the client. Having City be a struct doesn't help as cityId is never serialized. Any thoughts on how to get this to work?
     
  2. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    Just stumble across this posted, you may have already figured this out, but thought I would answer the question.

    Everything looks correct except the City cannot be a class and must be a struct before you can send over the network, the step you are missing is sending the data to the clients, many way of doing this but here is one example.

    Code (CSharp):
    1. public class Side : NetworkBehaviour {
    2.     public NetworkVariable<City> CitySide;
    3.     public override void OnNetworkSpawn () {
    4.         Debug.Log ($"<color=white>City Side ID: [<color=blue><b>{CitySide.Value.ID}</b></color>] Just Spawned</color>");
    5.     }
    6. }
    7.  
    8. public struct City : INetworkSerializable {
    9.     public uint ID;
    10.     public void NetworkSerialize<T> (BufferSerializer<T> serializer) where T : IReaderWriter {
    11.         serializer.SerializeValue (ref ID);
    12.     }
    13. }
    14.  
    15. if (NetworkManager.Singleton.IsServer) {
    16.     var go = GameObject.Instantiate (m_SpawnObject);
    17.     var com = go?.GetComponent<Side> ();
    18.     com.CitySide.Value = new City { ID = 1000 };
    19.     go?.GetComponent<NetworkObject> ()?.Spawn ();
    20. }
     
    Last edited: Dec 7, 2021
    cerestorm likes this.
  3. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    Another example, if you only wanting to send the City ID over the network.

    Code (CSharp):
    1.  public NetworkVariable<uint> CityID = new NetworkVariable<uint> ();
    2.     public override void OnNetworkSpawn () {
    3.         Debug.Log ($"<color=white>City Side ID: [<color=blue><b>{CityID.Value}</b></color>] Just Spawned</color>");
    4.     }
    5.  
    6. if (NetworkManager.Singleton.IsServer) {
    7.     var go = GameObject.Instantiate (m_SpawnObject);
    8.     var com = go?.GetComponent<Side> ();
    9.     com.CityID.Value = 1000;
    10.     go?.GetComponent<NetworkObject> ()?.Spawn ();
    11. }
    12.  
    Also in this example, updates the clients when ever the value changes on the server.

    Code (CSharp):
    1. public class Side : NetworkBehaviour {
    2.     private NetworkVariable<uint> CityID = new NetworkVariable<uint> ();
    3.     public override void OnNetworkDespawn () {
    4.         CityID.OnValueChanged -= valueChanged;
    5.     }
    6.     public override void OnNetworkSpawn () {
    7.         CityID.OnValueChanged += valueChanged;
    8.         Debug.Log ($"<color=white>City Side ID: [<color=blue><b>{CityID.Value}</b></color>] Just Spawned</color>");
    9.     }
    10.     private void ValueChanged (uint o, uint n) {
    11.         Debug.Log ($"<color=white>City ID Changed From Old: [<color=red><b>{o}</b></color>] To New: [<color=green><b>{n}</b></color>]</color>");
    12.     }
    13. }
    14.  
    15. if (NetworkManager.Singleton.IsServer) {
    16.     var go = GameObject.Instantiate (m_SpawnObject);
    17.     var com = go?.GetComponent<Side> ();
    18.     com.CityID.Value = 1000;
    19.     go?.GetComponent<NetworkObject> ()?.Spawn ();
    20. }
    There is still more ways to skin this cat, but these examples are quick and easy to implement.
     
    Last edited: Dec 7, 2021
    cerestorm likes this.
  4. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    I did solve the issue, but left it open in case of any interesting alternatives.

    This is what I'm doing currently, I've progressed a fair bit and City is now a network object itself.

    Code (CSharp):
    1. public class Side : NetworkBehaviour
    2. {
    3.     NetworkVariable<uint> sideId = new NetworkVariable<uint>();
    4.     NetworkVariable<ControlType> controlType = new NetworkVariable<ControlType>();
    5.     NetworkVariable<NetworkBehaviourReference> player = new NetworkVariable<NetworkBehaviourReference>();
    6.     NetworkVariable<NetworkBehaviourReference> city = new NetworkVariable<NetworkBehaviourReference>();
    7.     NetworkList<NetworkBehaviourReference> stacks;
    8.  
    9.     private void Awake()
    10.     {
    11.         // Unity complains if list is instantiated when declared
    12.         stacks = new NetworkList<NetworkBehaviourReference>();
    13.     }
    14.  
    15.     public uint SideId { get => sideId.Value; set => sideId.Value = value; }
    16.     public ControlType ControlType { get => controlType.Value; set => controlType.Value = value; }
    17.     public Player Player { get => (Player)player.Value; set => player.Value = value; }
    18.     public City City { get => (City)city.Value; set => city.Value = value; }
    19.     public NetworkList<NetworkBehaviourReference> Stacks { get => stacks; }
    20. }
    Code (CSharp):
    1. public class City : NetworkBehaviour
    2. {
    3.     NetworkVariable<uint> cityId = new NetworkVariable<uint>();
    4.     NetworkVariable<Vector2Int> position = new NetworkVariable<Vector2Int>();
    5.     NetworkVariable<NetworkBehaviourReference> side = new NetworkVariable<NetworkBehaviourReference>();
    6.  
    7.     public uint CityId { get => cityId.Value; set => cityId.Value = value; }
    8.     public Vector2Int Position { get => position.Value; set => position.Value = value; }
    9.     public Side Side { get => (Side)side.Value; set => side.Value = value; }
    10. }
    Thanks for your solutions, they don't fit my use case now but are not options I came up with so I'll keep them in mind. Structs are never my first go to (I blame my Java background), using them as non-nullable types for network variables makes a lot of sense.
     
    GreedyVox likes this.
  5. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    Awesome! Looks good! BTW nothing wrong with JAVA, its a nice language to program in.
     
    cerestorm likes this.