Search Unity

Question How to serialize a custom class?

Discussion in 'Netcode for GameObjects' started by NexiNex, Aug 3, 2023.

  1. NexiNex

    NexiNex

    Joined:
    Jun 4, 2020
    Posts:
    10
    My class is all the data needed for each player. It has lists, built in types, and a few methods, but those methods don't need to be transmitted and can be removed if it'll make implementation easier. It also has a list of a different class inside it, which is used for a major component of board state. The docs are not clear whatsoever, so, how would I go about serializing this data? I can provide the code if needed but the question looks general enough to me for no specific code to be necessary. I understand I can send each piece individually, and that's my backup if I can't just do this, but I would greatly prefer being able to send everything for the purposes of scalability.
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,981
    The minimal setup to get any class serialized as a serializable field (either public as in this case or attributed with [SerializeField]) is to simply attribute the class as [Serializable]:
    Code (CSharp):
    1. [System.Serializable]
    2. public class MyData
    3. {
    4.     public int exampleValue;
    5. }
    6.  
    7. public class MyBehaviour : MonoBehaviour
    8. {
    9.     public MyData data;
    10. }
    You can also use JsonUtility to serialize the fields of MyData manually to a json string/file.
    And if you want to take this further, Unity has a com.unity.serialization package that allows you to serialize to binary in a fast and convenient way.
     
  3. NexiNex

    NexiNex

    Joined:
    Jun 4, 2020
    Posts:
    10
    Sorry, I completely failed on providing a basic part of my question. How do I serialize it for the unity network? I need to be able to transmit it using a client rpc.
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,981
  5. NexiNex

    NexiNex

    Joined:
    Jun 4, 2020
    Posts:
    10
    Those examples are very clear how to implement a struct. I looked at them quite a bit before asking. I don't know how to make it work with class. Ref isn't allowed for class properties.
     
  6. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    To serialise a class field within a class you can create your classes like this:
    Code (CSharp):
    1. public class SomeData : INetworkSerializable
    2. {
    3.     int someValue;
    4.     MoreData moreData;
    5.  
    6.     public SomeData()
    7.     {
    8.     }
    9.  
    10.     public SomeData(int someValue, int anotherValue)
    11.     {
    12.         this.someValue = someValue;
    13.         moreData = new MoreData(anotherValue);
    14.     }
    15.  
    16.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    17.     {
    18.         serializer.SerializeValue(ref someValue);
    19.         serializer.SerializeNetworkSerializable(ref moreData);
    20.     }
    21.  
    22.     public override string ToString()
    23.     {
    24.         return someValue.ToString() + " " + moreData;
    25.     }
    26. }
    Code (CSharp):
    1. public class MoreData : INetworkSerializable
    2. {
    3.     int dataValue;
    4.  
    5.     public MoreData()
    6.     {
    7.     }
    8.  
    9.     public MoreData(int dataValue)
    10.     {
    11.         this.dataValue = dataValue;
    12.     }
    13.  
    14.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    15.     {
    16.         serializer.SerializeValue(ref dataValue);
    17.     }
    18.  
    19.     public override string ToString()
    20.     {
    21.         return dataValue.ToString();
    22.     }
    23. }
    You might want to share your code if you're doing something more complicated.
     
  7. NexiNex

    NexiNex

    Joined:
    Jun 4, 2020
    Posts:
    10
    One last problem, how do I serialize an array of a custom type? I get the error "The type 'serverScript.spell[]' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'BufferSerializer<T>.SerializeValue<T>(ref T, FastBufferWriter.ForPrimitives)'"
     
  8. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    Something along these lines will work:
    Code (CSharp):
    1. public class SomeData : INetworkSerializable
    2. {
    3.     int someValue;
    4.     MoreData moreData;
    5.     MoreData[] moreDataArray;
    6.  
    7.     public SomeData()
    8.     {
    9.     }
    10.  
    11.     public SomeData(int someValue, int anotherValue)
    12.     {
    13.         this.someValue = someValue;
    14.         moreData = new MoreData(anotherValue);
    15.  
    16.         moreDataArray = new MoreData[]
    17.         {
    18.             new MoreData(1),
    19.             new MoreData(2),
    20.             new MoreData(3),
    21.             new MoreData(4),
    22.             new MoreData(5)
    23.     };
    24.     }
    25.  
    26.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    27.     {
    28.         serializer.SerializeValue(ref someValue);
    29.         serializer.SerializeNetworkSerializable(ref moreData);
    30.  
    31.         if(serializer.IsWriter)
    32.         {
    33.             FastBufferWriter writer = serializer.GetFastBufferWriter();
    34.  
    35.             writer.WriteValueSafe(moreDataArray.Length);
    36.  
    37.             foreach (var data in moreDataArray)
    38.             {
    39.                 writer.WriteValueSafe(data);
    40.             }
    41.         }
    42.  
    43.         if (serializer.IsReader)
    44.         {
    45.             FastBufferReader reader = serializer.GetFastBufferReader();
    46.  
    47.             reader.ReadValueSafe(out int length);
    48.  
    49.             moreDataArray = new MoreData[length];
    50.  
    51.             for (int i = 0; i < length; i++)
    52.             {
    53.                 reader.ReadValueSafe(out moreDataArray[i]);
    54.             }
    55.         }
    56.     }
    57.  
    58.     public override string ToString()
    59.     {
    60.         StringBuilder stringBuilder = new StringBuilder("SomeData");
    61.         stringBuilder.Append(" someValue ").Append(someValue);
    62.         stringBuilder.Append(" moreData ").Append(moreData);
    63.  
    64.         foreach (var data in moreDataArray)
    65.         {
    66.             stringBuilder.Append(" arrayData ").Append(data);
    67.         }
    68.  
    69.         return stringBuilder.ToString();
    70.     }
    71. }
     
  9. abrakadouche

    abrakadouche

    Joined:
    Apr 8, 2022
    Posts:
    16
    Hi

    Just wondering if you ever got the array of custom serialization working. As I am having this exact issue right now.
     
  10. Pandazole

    Pandazole

    Joined:
    Sep 23, 2019
    Posts:
    28
    This is my current work on array serialization, which is quite similar to @cerestorm:

    Code (CSharp):
    1. public struct CustomStruct : INetworkSerializable
    2. {
    3.     public int A;
    4.     public int B;
    5.  
    6.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    7.     {
    8.         serializer.SerializeValue(ref A);
    9.         serializer.SerializeValue(ref B);
    10.     }
    11. }
    Code (CSharp):
    1. public struct AnotherStruct : INetworkSerializable
    2. {
    3.     CustomStruct[] cArray;
    4.  
    5.     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    6.     {
    7.         int n = cArray != null ? cArray.Length : 0;
    8.         serializer.SerializeValue(ref n);
    9.         if (serializer.IsReader)
    10.         {
    11.             cArray = new CustomStruct[n];
    12.         }
    13.         for (int i = 0; i < n; i++)
    14.         {
    15.             // Serialize each item in the array
    16.             serializer.SerializeValue(ref cArray[i]);
    17.         }
    18.     }
    19. }
    20.  
    I believe you can directly loop through the array with the serializer.SerializeValue if you are %100 sure that is not null :D.
     
  11. abrakadouche

    abrakadouche

    Joined:
    Apr 8, 2022
    Posts:
    16
    Oh, i have not touched any network related programming. Found out my issue was that unity doesn't support serializing multi dimensional arrays. I ended up figuring out a workaround.