docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Custom serialization

    Note

    Read the Serialization overview page to understand the basics of serialization before learning how to customize serialization.

    Netcode for GameObjects provide support for serializing any unsupported types, and with the API provided, it can even be done with types that you haven't defined yourself, those who are behind a 3rd party wall, such as .NET types. However, the way custom serialization is implemented for RPCs and NetworkVariables is slightly different.

    Netcode for GameObjects supports custom serialization of unsupported types, including those you haven't defined yourself, such as third-party .NET types. You can also use custom serialization to override how an existing supported type is serialized.

    Custom serialization is implemented slightly differently for RPCs and NetworkVariables. The examples on this page provide different ways to serialization a custom health struct.

    /// <summary>Container for storing health data for a player or item.</summary>
    public struct Health
    {
        /// <summary>
        /// The maximum health that this player or item can have.
        /// This is unlikely to change often.
        /// </summary>
        public uint MaxHealth;
    
        /// <summary>
        /// The current level of health that this player or item has.
        /// This is likely to change regularly.
        /// </summary>
        public int CurrentHealth;
    }
    

    FastBufferReader and FastBufferWriter

    FastBufferReader and FastBufferWriter are the main serialization tools in Netcode for GameObjects. To register serialization for a custom type, or override an already handled type, you need to create extension methods for FastBufferReader.ReadValueSafe() and FastBufferWriter.WriteValueSafe(). FastBufferReader and FastBufferWriter can read and write primitive types, and you can extend this functionality to serialize your custom type.

    /// <summary>Tells the Netcode how to serialize and deserialize our custom type.</summary>
    // The class name doesn't matter here.
    public static class FastBufferExtensions
    {
        /// <summary>
        /// Extension method to override the serialization for a custom type.
        /// </summary>
        /// <param name="writer">Buffer to write values into.</param>
        /// <param name="health">The type to customize or override.</param>
        public static void WriteValueSafe(this FastBufferWriter writer, in Health health)
        {
            writer.WriteValueSafe(health.MaxHealth);
            writer.WriteValueSafe(health.CurrentHealth);
        }
    
        /// <summary>
        /// Extension method to override the de-serialization for a custom type.
        /// </summary>
        /// <param name="reader">Buffer to read values from.</param>
        /// <param name="health">The type to customize or override.</param>
        public static void ReadValueSafe(this FastBufferReader reader, out Health health)
        {
            reader.ReadValueSafe(out uint max);
            reader.ReadValueSafe(out int current);
            health = new Health { MaxHealth = max, CurrentHealth = current };
        }
    }
    

    You may also need to add extensions for FastBufferReader.ReadValue(), FastBufferWriter.WriteValue() if you want to serialize without bounds checking

    BufferSerializer

    You can also add custom serialization support to the bi-directional BufferSerializer. This makes your custom type readily available within INetworkSerializable types and in the NetworkBehaviour.OnSynchronize() method:

    /// <summary>Tells the <see cref="BufferSerializer{TReaderWriter}"/> how to serialize and deserialize our custom type.</summary>
    // The class name doesn't matter here.
    public static class BufferSerializerExtensions
    {
        /// <summary>
        /// Extension method to override bi-directional serialization for a custom type.
        /// </summary>
        /// <param name="serializer">Bi-directional serial</param>
        /// <param name="health">The type to customize or override.</param>
        /// <typeparam name="TReaderWriter">Boilerplate syntax to enable the bi-directional serialization.</typeparam>
        public static void SerializeValue<TReaderWriter>(this BufferSerializer<TReaderWriter> serializer, ref Health health) where TReaderWriter : IReaderWriter
        {
            // Because the BufferSerializer already knows how to read and write the primitive types
            // We can use the existing BufferSerializer serialization.
            serializer.SerializeValue(ref health.MaxHealth);
            serializer.SerializeValue(ref health.CurrentHealth);
        }
    }
    

    Remote procedure call (RPCs)

    Note

    RPCs can use the Network Variable flow, but NetworkVariables can't use the RPC flow. The RPC flow is more efficient when only RPCs need to serialize the type. When a type is used by both NetworkVariables and RPCs, you can implement just the NetworkVariable flow to lower maintenance requirements. Unity will select the RPC flow for RPCs if you have implemented both flows.

    To serialize a custom type, or override an already handled type, you need to create extension methods for FastBufferReader.ReadValueSafe() and FastBufferWriter.WriteValueSafe() as outlined above.

    The code generation for RPCs will automatically pick up and use these functions, as they'll become available via FastBufferWriter and FastBufferReader directly.

    NetworkVariable

    Implementing INetworkSerializable is the cleanest and most straightforward way to customize the serialization of a type within a NetworkVariable. UserNetworkVariableSerialization provides runtime configuration to further override serialization of a type.

    First you will need to create extension methods for FastBufferReader.ReadValueSafe() and FastBufferWriter.WriteValueSafe() as outlined above.

    Secondly, somewhere in your application startup (before any NetworkVariables using the affected types will be serialized), add the following:

    UserNetworkVariableSerialization<Health>.WriteValue = SerializationExtensions.WriteValueSafe;
    UserNetworkVariableSerialization<Health>.ReadValue = SerializationExtensions.ReadValueSafe;
    UserNetworkVariableSerialization<Health>.DuplicateValue = (in Health value, ref Health duplicatedValue) => duplicatedValue = value;
    

    DuplicateValue should return a complete deep copy of the value that NetworkVariable<T> compares to a previous value, which is used to check whether the value has changed. DuplicateValue avoids re-serializing it over the network every frame when it hasn't changed.

    Note

    WriteValue, ReadValue and DuplicateValue all need to be defined to customize your serialization.

    Note

    WriteValue and ReadValue will not be used if a type implements INetworkSerializable or INetworkSerializeByMemcpy.

    Serializing delta updates

    Reading and writing a value provides the minimal amount of NetworkVariable functionality. This will synchronize your whole type whenever any value within the type value changes. To support sending delta updates rather than a full update whenever your type has changed, implement the following functions:

    • WriteDelta
    • ReadDelta
    Note

    Both WriteDelta and ReadDelta need to be defined for either to be used.

    Here is a full implementation of a custom type with the methods needed for UserNetworkVariableSerialization

    Warning

    It looks like the sample you are looking for does not exist.

    In This Article
    Back to top
    Copyright © 2026 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)