-
Notifications
You must be signed in to change notification settings - Fork 461
fix: Serialization docs #4007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop-2.0.0
Are you sure you want to change the base?
fix: Serialization docs #4007
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -70,15 +70,17 @@ | |||||
| * [Network update loop reference](advanced-topics/network-update-loop-system/network-update-loop-reference.md) | ||||||
| * [Network time and ticks](advanced-topics/networktime-ticks.md) | ||||||
| * [Serialization](serialization.md) | ||||||
| * [Serialization overview](advanced-topics/serialization/serialization-overview.md) | ||||||
| * [C# primitives](advanced-topics/serialization/cprimitives.md) | ||||||
| * [Unity primitives](advanced-topics/serialization/unity-primitives.md) | ||||||
| * [Enum types](advanced-topics/serialization/enum-types.md) | ||||||
| * [Arrays](advanced-topics/serialization/serialization-arrays.md) | ||||||
| * [INetworkSerializable](advanced-topics/serialization/inetworkserializable.md) | ||||||
| * [INetworkSerializeByMemcpy](advanced-topics/serialization/inetworkserializebymemcpy.md) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| * [Custom serialization](advanced-topics/custom-serialization.md) | ||||||
| * [NetworkObject serialization](advanced-topics/serialization/networkobject-serialization.md) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| * [FastBufferWriter and FastBufferReader](advanced-topics/fastbufferwriter-fastbufferreader.md) | ||||||
| * [BufferSerializer](advanced-topics/bufferserializer.md) | ||||||
| * [Custom serialization](advanced-topics/custom-serialization.md) | ||||||
| * [Scene management](scene-management.md) | ||||||
| * [Scene management overview](basics/scenemanagement/scene-management-overview.md) | ||||||
| * [Integrated management](integrated-management.md) | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||
| # BufferSerializer | ||||||||
|
|
||||||||
| It's recommended to read the [serialization overview](./serialization/serialization-overview.md) before reading this documentation. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| `BufferSerializer<TReaderWriter>` is the bi-directional serializer primarily used for serializing within [`INetworkSerializable`](serialization/inetworkserializable.md) types. It wraps [`FastBufferWriter` and `FastBufferReader`](fastbufferwriter-fastbufferreader.md) to provide high performance serialization, but has a couple of differences to make it more user-friendly: | ||||||||
|
|
||||||||
| - Rather than writing separate methods for serializing and deserializing, `BufferSerializer<TReaderWriter>` allows writing a single method that can handle both operations, which reduces the possibility of a mismatch between the two | ||||||||
|
|
@@ -15,3 +17,7 @@ However, when those downsides are unreasonable, `BufferSerializer<TReaderWriter> | |||||||
|
|
||||||||
| - For performance, you can use `PreCheck(int amount)` followed by `SerializeValuePreChecked()` to perform bounds checking for multiple fields at once. | ||||||||
| - For both performance and bandwidth usage, you can obtain the wrapped underlying reader/writer via `serializer.GetFastBufferReader()` when `serializer.IsReader` is `true`, and `serializer.GetFastBufferWriter()` when `serializer.IsWriter` is `true`. These provide micro-performance improvements by removing a level of indirection, and also give you a type you can use with `BytePacker` and `ByteUnpacker`. | ||||||||
|
|
||||||||
| ## Serializing custom types | ||||||||
|
|
||||||||
| `BufferSerializer<TReaderWriter>` can be extended via extension methods to handle serializing custom types. Refer to [customizing `BufferSerializer`](./custom-serialization.md#bufferserializer) for instructions on how to do this. | ||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,93 +1,68 @@ | ||||||||||
| # Custom serialization | ||||||||||
|
|
||||||||||
| Netcode uses a default serialization pipeline when using `RPC`s, `NetworkVariable`s, or any other Netcode-related tasks that require serialization. The serialization pipeline looks like this: | ||||||||||
| Before reading these docs, ensure you have read the [serialization overview](./serialization/serialization-overview.md) | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| `` | ||||||||||
| Custom Types => Built In Types => INetworkSerializable | ||||||||||
| `` | ||||||||||
| 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. | ||||||||||
|
|
||||||||||
| That is, when Netcode first gets hold of a type, it will check for any custom types that the user have registered for serialization, after that it will check if it's a built in type, such as a Vector3, float etc. These are handled by default. If not, it will check if the type inherits `INetworkSerializable`, if it does, it will call it's write methods. | ||||||||||
| Let's explore different ways to implement custom serialization for a custom health struct. | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| By default, any type that satisfies the `unmanaged` generic constraint can be automatically serialized as RPC parameters. This includes all basic types (bool, byte, int, float, enum, etc) as well as any structs that has only these basic types. | ||||||||||
| [!code-cs[](../../Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs#HealthStruct)] | ||||||||||
|
|
||||||||||
| With this flow, you can 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. | ||||||||||
| ## FastBufferReader and FastBufferWriter | ||||||||||
|
|
||||||||||
| ### Serialize a type in a Remote Procedure Call (RPC) | ||||||||||
| [`FastBufferReader` and `FastBufferWriter`](./fastbufferwriter-fastbufferreader.md) 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()`. Because the `FastBufferReader` and `FastBufferWriter` already know how to read and write primitive types you want to use this functionality to serialize your custom type. | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| > [!NOTE] | ||||||||||
| > From versioln 1.7.0 Remote Procedure Calls (RPCs) can also use the Network Variable flow, but NetworkVariables can't use the RPC flow. The RPC flow is more efficient when RPCs serialize the type. Unity selects the RPC flow if you implement both the RPC and Network variable flows. When a type is used by both NetworkVariables and RPCs you can use the NetworkVariable flow to lower maintenance requirements. | ||||||||||
| [!code-cs[](../../Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs#FastBuffer)] | ||||||||||
|
|
||||||||||
| To register a custom type, or override an already handled type, you need to create extension methods for `FastBufferReader.ReadValueSafe()` and `FastBufferWriter.WriteValueSafe()`: | ||||||||||
| Additionally, you may also need to add extensions for `FastBufferReader.ReadValue()`, `FastBufferWriter.WriteValue()` if you would like to provide for serialization without [bounds checking](./fastbufferwriter-fastbufferreader.md#bounds-checking) | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ```csharp | ||||||||||
| // Tells the Netcode how to serialize and deserialize Url in the future. | ||||||||||
| // The class name doesn't matter here. | ||||||||||
| public static class SerializationExtensions | ||||||||||
| { | ||||||||||
| public static void ReadValueSafe(this FastBufferReader reader, out Url url) | ||||||||||
| { | ||||||||||
| reader.ReadValueSafe(out string val); | ||||||||||
| url = new Url(val); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| public static void WriteValueSafe(this FastBufferWriter writer, in Url url) | ||||||||||
| { | ||||||||||
| writer.WriteValueSafe(url.Value); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| ``` | ||||||||||
| ## BufferSerializer | ||||||||||
|
|
||||||||||
| The code generation for RPCs will automatically pick up and use these functions, and they'll become available via `FastBufferWriter` and `FastBufferReader` directly. | ||||||||||
| You can also add custom serialization support to the bi-directional [`BufferSerializer`](./bufferserializer.md). This will make this type readily available within [`INetworkSerializable`](serialization/inetworkserializable.md) types and in the [`NetworkBehaviour.OnSynchronize()` method](../components/core/networkbehaviour-synchronize.md#prespawn-synchronization-with-onsynchronize): | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| You can also optionally use the same method to add support for `BufferSerializer<TReaderWriter>.SerializeValue()`, if you wish, which will make this type readily available within [`INetworkSerializable`](serialization/inetworkserializable.md) types: | ||||||||||
| [!code-cs[](../../Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs#BufferSerializer)] | ||||||||||
|
|
||||||||||
| ```csharp | ||||||||||
| // The class name doesn't matter here. | ||||||||||
| public static class SerializationExtensions | ||||||||||
| { | ||||||||||
| public static void SerializeValue<TReaderWriter>(this BufferSerializer<TReaderWriter> serializer, ref Url url) where TReaderWriter: IReaderWriter | ||||||||||
| { | ||||||||||
| if (serializer.IsReader) | ||||||||||
| { | ||||||||||
| url = new Url(); | ||||||||||
| } | ||||||||||
| serializer.SerializeValue(ref url.Value); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| ``` | ||||||||||
| ## Remote Procedure Call (RPC) | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| Additionally, you can also add extensions for `FastBufferReader.ReadValue()`, `FastBufferWriter.WriteValue()`, and `BufferSerializer<TReaderWriter>.SerializeValuePreChecked()` to provide more optimal implementations for manual serialization using `FastBufferReader.TryBeginRead()`, `FastBufferWriter.TryBeginWrite()`, and `BufferSerializer<TReaderWriter>.PreCheck()`, respectively. However, none of these will be used for serializing RPCs - only `ReadValueSafe` and `WriteValueSafe` are used. | ||||||||||
| > [!NOTE] | ||||||||||
| > Remote Procedure Calls (RPCs) can also 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. | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ### For NetworkVariable | ||||||||||
| 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](#fastbufferreader-and-fastbufferwriter). | ||||||||||
|
|
||||||||||
| `NetworkVariable` goes through a slightly different pipeline than `RPC`s and relies on a different process for determining how to serialize its types. As a result, making a custom type available to the `RPC` pipeline doesn't automatically make it available to the `NetworkVariable` pipeline, and vice-versa. The same method can be used for both, but currently, `NetworkVariable` requires an additional runtime step to make it aware of the methods. | ||||||||||
| The code generation for RPCs will automatically pick up and use these functions, as they'll become available via `FastBufferWriter` and `FastBufferReader` directly. | ||||||||||
|
|
||||||||||
| To add custom serialization support in `NetworkVariable`, follow the steps from the "For RPCs" section to write extension methods for `FastBufferReader` and `FastBufferWriter`; then, somewhere in your application startup (before any `NetworkVariable`s using the affected types will be serialized) add the following: | ||||||||||
| ## NetworkVariable | ||||||||||
|
|
||||||||||
| ```csharp | ||||||||||
| UserNetworkVariableSerialization<Url>.WriteValue = SerializationExtensions.WriteValueSafe; | ||||||||||
| UserNetworkVariableSerialization<Url>.ReadValue = SerializationExtensions.ReadValueSafe; | ||||||||||
| ``` | ||||||||||
| Implementing [`INetworkSerializable`](./serialization/inetworkserializable.md) is the cleanest and most straightforward way to customize the serialization on a type within a [`NetworkVariable`](../basics/networkvariable.md). `UserNetworkVariableSerialization` provides runtime configuration to further override serialization of a type. | ||||||||||
|
|
||||||||||
|
Comment on lines
+36
to
+37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| First you will need to create extension methods for `FastBufferReader.ReadValueSafe()` and `FastBufferWriter.WriteValueSafe()` as [outlined above](#fastbufferreader-and-fastbufferwriter). | ||||||||||
|
|
||||||||||
| You can also use lambda expressions here: | ||||||||||
| Secondly, somewhere in your application startup (before any `NetworkVariable`s using the affected types will be serialized), add the following: | ||||||||||
|
|
||||||||||
| ```csharp | ||||||||||
| UserNetworkVariableSerialization<Url>.WriteValue = (FastBufferWriter writer, in Url url) => | ||||||||||
| { | ||||||||||
| writer.WriteValueSafe(url.Value); | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| UserNetworkVariableSerialization<Url>.ReadValue = (FastBufferReader reader, out Url url) | ||||||||||
| { | ||||||||||
| reader.ReadValueSafe(out string val); | ||||||||||
| url = new Url(val); | ||||||||||
| }; | ||||||||||
| UserNetworkVariableSerialization<Health>.WriteValue = SerializationExtensions.WriteValueSafe; | ||||||||||
| UserNetworkVariableSerialization<Health>.ReadValue = SerializationExtensions.ReadValueSafe; | ||||||||||
| UserNetworkVariableSerialization<Health>.DuplicateValue = (in Health value, ref Health duplicatedValue) => duplicatedValue = value; | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| When you create an extension method in `NetworkVariable<T>` you need to implement the following values: | ||||||||||
| `DuplicateValue` should return a complete deep copy of the value that `NetworkVariable<T>` compares to a previous value. It is used to check whether the value has changed. `DuplicateValue` avoids re-serializing it over the network every frame when it hasn't changed. | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| > [!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`](./serialization/inetworkserializebymemcpy.md). | ||||||||||
|
|
||||||||||
| ### Serializing delta updates | ||||||||||
|
|
||||||||||
| Simply reading and writing a value will provide the minimal amount of `NetworkVariable` functionality. This will synchronize your whole type any time any value within the type value changes. To provide sending delta updates rather than a full updates whenever your type has changed, implement the following functions: | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| - `WriteDelta` | ||||||||||
| - `ReadDelta` | ||||||||||
|
|
||||||||||
| > [!NOTE] | ||||||||||
| > Both `WriteDelta` and `ReadDelta` need to be defined for either to be used. | ||||||||||
|
|
||||||||||
| - `WriteValue` | ||||||||||
| - `ReadValue` | ||||||||||
| - `DuplicateValue` | ||||||||||
| Here is a full implementation of a custom type with the methods needed for `UserNetworkVariableSerialization` | ||||||||||
|
|
||||||||||
| `DuplicateValue` returns a complete deep copy of the value that `NetworkVariable<T>` compares to a previous value to check whether or not that values has changed. This avoids reserializing it over the network every frame when it hasn't changed. | ||||||||||
| [!code-cs[](../../Tests/Runtime/DocumentationCodeSamples/NetworkVariable/NetworkVariableSerialization.cs#HealthExample)] | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||
| # FastBufferWriter and FastBufferReader | ||||||||
|
|
||||||||
| It's recommended to read the [serialization overview](./serialization/serialization-overview.md) before reading this documentation. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| The serialization and deserialization is done via `FastBufferWriter` and `FastBufferReader`. These have methods for serializing individual types and methods for serializing packed numbers, but in particular provide a high-performance method called `WriteValue()/ReadValue()` (for Writers and Readers, respectively) that can extremely quickly write an entire unmanaged struct to a buffer. | ||||||||
|
|
||||||||
| There's a trade-off of CPU usage vs bandwidth in using this: Writing individual fields is slower (especially when it includes operations on unaligned memory), but allows the buffer to be filled more efficiently, both because it avoids padding for alignment in structs, and because it allows you to use `BytePacker.WriteValuePacked()`/`ByteUnpacker.ReadValuePacked()` and `BytePacker.WriteValueBitPacked()`/`ByteUnpacker.ReadValueBitPacked()`. The difference between these two is that the BitPacked variants pack more efficiently, but they reduce the valid range of values. See the section below for details on packing. | ||||||||
|
|
@@ -156,3 +158,7 @@ Packing values is done using the utility classes `BytePacker` and `ByteUnpacker` | |||||||
| | uint | 30 bits (0 to 1,073,741,824) | | ||||||||
| | long | 60 bits + sign bit (-1,152,921,504,606,846,976 to 1,152,921,504,606,846,975) | | ||||||||
| | ulong | 61 bits (0 to 2,305,843,009,213,693,952) | | ||||||||
|
|
||||||||
| ## Serializing custom types | ||||||||
|
|
||||||||
| `FastBufferReader` and `FastBufferWriter` can be extended via extension methods to handle serializing custom types. Refer to [customizing `FastBufferReader` and `FastBufferWriter`](./custom-serialization.md#fastbufferreader-and-fastbufferwriter) for instructions on how to do this. | ||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,13 @@ | ||||||||
| # INetworkSerializable | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| You can use the `INetworkSerializable` interface to define custom serializable types. | ||||||||
| It's recommended to read the [serialization overview](./serialization/serialization-overview.md) to better understand this documentation. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| You can use the `INetworkSerializable` interface to define custom serializable types. This interface has one function: `NetworkSerialize<T>(BufferSerializer<T> serializer)`. This function is provided a bi-directional [`BufferSerializer`](../bufferserializer.md) that you can use to implement bi-directional custom serialization. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| `INetworkSerializable` can be implemented on both unmanaged types *and* managed types. However, we recommend avoiding serializing managed types where possible to improve your game's performance. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| ```csharp | ||||||||
| struct MyComplexStruct : INetworkSerializable | ||||||||
| struct SpawnPoint : INetworkSerializable | ||||||||
| { | ||||||||
| public Vector3 Position; | ||||||||
| public Quaternion Rotation; | ||||||||
|
|
@@ -18,24 +22,20 @@ struct MyComplexStruct : INetworkSerializable | |||||||
| } | ||||||||
| ``` | ||||||||
|
|
||||||||
| Types implementing `INetworkSerializable` are supported by `NetworkSerializer`, `RPC` s and `NetworkVariable` s. | ||||||||
| Types implementing `INetworkSerializable` are supported by [`FastBufferReader` and `FastBufferWriter`](./fastbufferwriter-fastbufferreader.md), [`RPC`s'](../message-system/rpc.md), and [`NetworkVariable`s](../../basics/networkvariable.md). | ||||||||
|
|
||||||||
| ```csharp | ||||||||
|
|
||||||||
| [Rpc(SendTo.Server)] | ||||||||
| void MyServerRpc(MyComplexStruct myStruct) { /* ... */ } | ||||||||
| void SpawnAtPointRpc(SpawnPoint spawnPoint) { /* ... */ } | ||||||||
|
|
||||||||
| void Update() | ||||||||
| void DoSpawnHere() | ||||||||
| { | ||||||||
| if (Input.GetKeyDown(KeyCode.P)) | ||||||||
| var spawnPoint = new SpawnPoint | ||||||||
| { | ||||||||
| MyServerRpc( | ||||||||
| new MyComplexStruct | ||||||||
| { | ||||||||
| Position = transform.position, | ||||||||
| Rotation = transform.rotation | ||||||||
| }); // Client -> Server | ||||||||
| } | ||||||||
| Position = transform.position, | ||||||||
| Rotation = transform.rotation | ||||||||
| }; | ||||||||
| SpawnAtPointRpc(spawnPoint); // Client -> Server | ||||||||
| } | ||||||||
| ``` | ||||||||
|
|
||||||||
|
|
@@ -59,7 +59,7 @@ The following example explores a more advanced use case. | |||||||
|
|
||||||||
| ```csharp | ||||||||
|
|
||||||||
| public struct MyMoveStruct : INetworkSerializable | ||||||||
| public struct SpawnWithMovement : INetworkSerializable | ||||||||
| { | ||||||||
| public Vector3 Position; | ||||||||
| public Quaternion Rotation; | ||||||||
|
|
@@ -125,7 +125,7 @@ Review the following example: | |||||||
|
|
||||||||
| ```csharp | ||||||||
|
|
||||||||
| public struct MyStructA : INetworkSerializable | ||||||||
| public struct SpawnPoint : INetworkSerializable | ||||||||
| { | ||||||||
| public Vector3 Position; | ||||||||
| public Quaternion Rotation; | ||||||||
|
|
@@ -137,17 +137,17 @@ public struct MyStructA : INetworkSerializable | |||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| public struct MyStructB : INetworkSerializable | ||||||||
| public struct SpawnInfo : INetworkSerializable | ||||||||
| { | ||||||||
| public int SomeNumber; | ||||||||
| public string SomeText; | ||||||||
| public MyStructA StructA; | ||||||||
| public SpawnPoint SpawnPoint; | ||||||||
|
|
||||||||
| void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter | ||||||||
| { | ||||||||
| serializer.SerializeValue(ref SomeNumber); | ||||||||
| serializer.SerializeValue(ref SomeText); | ||||||||
| StructA.NetworkSerialize(serializer); | ||||||||
| SpawnPoint.NetworkSerialize(serializer); | ||||||||
| } | ||||||||
| } | ||||||||
| ``` | ||||||||
|
|
@@ -167,7 +167,7 @@ While you can have nested `INetworkSerializable` implementations (an `INetworkSe | |||||||
|
|
||||||||
| ```csharp | ||||||||
| /// This isn't supported. | ||||||||
| public struct MyStructB : MyStructA | ||||||||
| public struct SpawnInfo : SpawnPoint | ||||||||
| { | ||||||||
| public int SomeNumber; | ||||||||
| public string SomeText; | ||||||||
|
|
@@ -232,4 +232,4 @@ Then declare this network variable like so: | |||||||
|
|
||||||||
| ```csharp | ||||||||
| NetworkVariable<GameDataWithLong> myVar = new NetworkVariable<GameDataWithLong>(); | ||||||||
| ``` | ||||||||
| ``` | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.