STATE SYNCHRONIZATION DETAILS

STATE SYNCHRONIZATION DETAILS

For data that needs to be synchronized over the network very often, the state synchronization feature in uLink is commonly used. You can enable state synchronization on a game object or prefab by adding the uLinkNetworkView script component. Then, choose either Reliable Delta Compressed, Reliable or Unreliable from the drop-down menu for the State Synchronization property.
A network view can only send RPCs and state-sync to other network views with identical uLink.NetworkViewIDs. Therefore it is very important to make sure that view IDs match up correctly. Using uLink.Network.Instantiate() to create the objects will automatically set the same view ID on all created instances of an object. Use this method as often as you can. Read more in the Instantiating Network-Aware Objects manual chapter.
You must also choose what kind of data should be synchronized in the Observed property. uLink state synchronization can pack/unpack some specific classes: Transform, Rigidbody, Animation and uLink.MonoBehaviour (scripts). Choose one of these as the Observed property.
  • Transforms are serialized by storing position, rotation and scale. Parenting information is not transferred over the network.
  • Rigidbody serializes position, rotation, velocity and angular velocity.
  • Animation serializes each running animation state, that is time, weight, speed and whether it is enabled or not. Note that this is a very inefficient and error-prone way of animating network-aware objects. We recommend that you only send orientation and velocity data as part of the state-synch and that you calculate the animation state locally based on this data. Use the uLinkSmoothCharacter utility script as an aid in this process.
  • uLink.MonoBehaviours invoke the callback uLink.Network.uLink_OnSerializeNetworkView() on serialization and deserialization. This lets you program custom serializations.
If you want to use synchronize more than one component, add the uLinkObservedList component to the game object and drag it to the Observed property of the uLinkNetworkView. Set the length of the list to the number of components you want to synchronize for the game object and then drag each component to its own slot. Now uLink will synchronize everything specified in the list.

Synchronizing script components

For a custom script component to be serialized and deserialized, uLink invokes the uLink.Network.uLink_OnSerializeNetworkView() callback. Like all uLink callbacks, this requires the component class to inherit uLink.MonoBehaviour. When a game object with an attached network view is told by uLink either to serialize and send a state-synch, or to deserialize a received state-synch and update its state, this callback is executed. This means that when implementing the callback in a script you have to remember to handle both the serialization and deserialization cases. The first parameter to the callback is a uLink.BitStream, which should be used both as target for serialization and source for deserialization. You can also find out if the callback was called to do serialization or deserialization by inspecting the uLink.BitStream.isWriting or uLink.BitStream.isReading fields. You only have to check one of them as they are mutually exclusive.
For serialization to be enabled for a given component, you must remember to drag it from the inspector view to the Observed property either on the uLinkNetworkView, or on a uLinkObservedList component which in turn has been dragged to the Observed property of the uLinkNetworkView.
Below is a basic example of how to implement the callback in a minimal script. The methods used here are the ones recommended for normal use. There are many variations of the reading and writing methods in uLink.BitStream that have specific advanced purposes. The example shows a Health component that keeps track of various health-stats of a character. It does not show any game logic, only the data fields and the state-synch code.
using uLink; using UnityEngine; public class Health : uLink.MonoBehaviour { private int healthPoints; private int healthPointsMax; private bool crippled; private bool blinded; void uLink_OnSerializeNetworkView(uLink.BitStream stream, uLink.NetworkMessageInfo info) { // Note that healthPointsMax is never state-synched, because it is assumed // to never change. // Check if we should write to the stream, or read from the stream. if (stream.isWriting) { // This is performed on serialization. stream.Write(healthPoints); stream.Write(crippled); stream.Write(blinded); } else { // This is performed on deserialization. healthPoints = stream.Read<int>(); crippled = stream.Read<bool>(); blinded = stream.Read<bool>(); } } }
There are some interesting things to note in this example. First, not all data of the component is state-synched; the healthPointsMax field is not present in the serialization. This illustrates a very important concept, that you can, and should, choose very carefully what data should be sent over the network. In the callback, we have explicitly called the