[UnityManual] Unity Manual#Multiplayer and Networking#The High Level API#State Synchronization
本帖最后由 boswellyu 于 2016-2-5 09:27 编辑

    State Synchronization
    状态同步

State Synchronization is done from the Server to Remote Clients. The local client does not have data serialized to it, since it shares the scene with the server. Any data serialized to a local client would be redundant. SyncVar hooks however are called on local clients.
状态同步是从服务器向客户端方向上的。本地客户端没有序列化的数据,因为它和服务器共享同一个场景。任何为本地客户端序列化的数据都是多余的。然而,SyncVar钩子函数会被本地客户端调用。

Data is not synchronized from remote clients to the server. This is job of Commands.
数据不会从客户端向服务器同步,这个方向上的操作叫做命令(Commands)。

SyncVars
同步变量

SyncVars are member variables of NetworkBehaviour scripts that are synchronized from the server to clients. When an object is spawned, or a new player joins a game in progress, they are sent the latest state of all SyncVars on networked objects that are visible to them. Member variables are made into SyncVars by using the [SyncVar] custom attribute:
同步变量是NetworkBehaviour脚本中的成员变量,他们会从服务器同步到客户端上。当一个物体被派生出来之后,或者一个新的玩家中途加入游戏后,他会接收到他的视野内所有物体的同步变量。成员变量通过[SyncVar]标签被配置成同步变量:

[mw_shl_code=csharp,true]class Player : NetworkBehaviour
{

    [SyncVar]
    int health;

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;

        health -= amount;
    }
}[/mw_shl_code]

The state of SyncVars is applied to objects on clients before OnStartClient() is called, so the state of the object is guaranteed to be up-to-date inside OnStartClient().
同步变量的状态在OnStartClient()之前就被应用到物体上了,所以在OnStartClient函数中,物体的状态已经是最新的数据。

SyncVars can be basic types such as integers, strings and floats. They can also be Unity types such as Vector3 and user-defined structs, but updates for struct SyncVars are sent as monolithic updates, not incremental changes if fields within a struct change. There can be up to 32 SyncVars on a single NetworkBehaviour script - this includes SyncLists.
同步变量可以是基础类型,如整数,字符串和浮点数。也可以是Unity内置数据类型,如Vector3和用户自定义的结构体,但是对结构体类型的同步变量,如果只有几个字段的数值有变化,整个结构体都会被发送。每个NetworkBehaviour脚本可以有最多32个同步变量,包括同步列表(见下面的解释)。

SycnVar updates are sent automatically by the server when the value of a SyncVar changes. There is no need to perform any manual dirtying of fields for SyncVars.
当同步变量有变化时,服务器会自动发送他们的最新数据。不需要手工为同步变量设置任何的脏数据标志位。

Note that setting a SyncVar member variable inside a property setter function does not cause it to be dirtied. Trying to do this will cause a compile time warning. Because SyncVars use properties internally to mark themselves as dirty, setting them dirty inside property functions could lead to recursion problems.
注意在属性设置函数中设置一个同步变量的值不会使他的脏数据标志被设置。如果这样做的话,会得到一个编译期的警告。因为同步变量使用他们自己内部的标识记录脏数据状态,在属性设置函数中设置脏位会引起递归调用问题


SyncLists
同步列表

SyncLists are like SyncVars but they are lists of values instead of individual values. SyncList contents are included in initial state updates with SyncVar state. SyncLists do not require the SyncVar attributes, they are specific classes. There are built-in SyncList types for basic types:
同步列表类似于同步变量,但是他们是一些值的列表而不是单个值。同步列表和同步变量都包含在初始的状态更新里。同步列表不需要[SyncVar]属性标识,他们是特殊的类。内建的基础类型属性列表有:

  • SyncListString
  • SyncListFloat
  • SyncListInt
  • SyncListUInt
  • SyncListBool

There is also SyncListStruct which can be used for lists of user-defined structs. The struct used SyncListStruct derived class can contain members of basic types, arrays, and common Unity types. They cannot contain complex classes or generic containers.
还有个SyncListStruct可以给用户自定义的结构体用。从SyncListStruct派生出的结构体类可以包含基础类型,数组和通用Unity类型的成员变量,但是不能包含复杂的类和通用容器。

SyncLists have a SyncListChanged delegate named Callback that allows clients to be notified when the contents of the list change. This delegate is called with the type of operation that occurred, and th eindex of the item that the operation was for.
同步列表有一个叫做SyncListChanged的回调函数,可以使客户端能接收到列表中的数据改动的通知。这个回调函数被调用时,会被通知到操作类型,和修改的变量索引。

[mw_shl_code=csharp,true]public class MyScript : NetworkBehaviour
{
    public struct Buf
    {
        public int id;
        public string name;
        public float timer;
    };
            
    public class TestBufs : SyncListStruct<Buf> {}
    TestBufs m_bufs = new TestBufs();
   
    void BufChanged(Operation op, int itemIndex)
    {
        Debug.Log("buf changed:" + op);
    }
   
    void Start()
    {
        m_bufs.Callback = BufChanged;
    }
}[/mw_shl_code]

Custom Serialization Functions
定制序列化函数

Often the use of SyncVars is enough for scripts to serialize their state to clients, but some cases require more complex serialization code. The virtual functions on NetworkBehaviour that are used for SyncVar serialization can be implmented by developers to perform their own custom serialization. These functions are:
通常在脚本中使用同步变量就够了,但是有时候也需要更复杂的序列化代码。NetworkBehaviour中的虚函数允许开发者定制自己的序列化函数,这些函数有:

[mw_shl_code=csharp,true]public virtual bool OnSerialize(NetworkWriter writer, bool initialState);
public virtual void OnDeSerialize(NetworkReader reader, bool initialState);[/mw_shl_code]

The initialState flag is useful to differentiate between the first time an object is serialized and when incremental updates can be sent. The first time an object is sent to a client, it must include a full state snapshot, but subsequent updates can save on bandwidth by including only incremental changes. Note that SyncVar hook fucntion are not called when initialState is true, only for incremental updates.
initalState可以用来标识是第一次序列化数据还是只发送增量的数据。如果是第一次发送给客户端,必须要包含所有状态的数据,后续的更新只需要包含增量的修改,以节省带宽。同步变量的钩子函数在initialState为True的时候不会被调用,而只会在增量更新函数中被调用。

If a class has SyncVars, then implementations of these functions are added automatically to the class. So a class that has SyncVars cannot also have custom serialization functions.
如果一个类里面声明了同步变量,这些函数的实现会自动被加到类里面,因此一个有同步变量的类不能拥有自己的序列化函数。

The OnSerialize function should return true to indicate that an update should be sent. If it returns true, then the dirty bits for that script are set to zero, if it returns false then the dirty bits are not changed. This allows multiple changes to a script to be accumulated over time and sent when the system is ready, instead of every frame.
OnSerialize函数应该返回True来指示有更新需要发送,如果它返回了true,这个类的所有脏标志位都会被清除,如果它返回False,则脏标志位不会被修改。这可以允许将多次改动合并在一起发送,而不需要每一帧都发送。

Serialization Flow
序列化流程

Game objects with the NetworkIdentity component can have multiple scripts derived from NetworkBehaviour. The flow for serializing these objects is:
具有NetworkIdentity组件的游戏物体可以带有多个从NetworkBehaviour派生出来的脚本,这些物体的序列化流程为:

On the server:
在服务器上:
  • Each NetworkBehaviour has a dirty mask. This mask is available inside OnSerialize as syncVarDirtyBits
  • 每个NetworkBehaviour上都有一个脏数据掩码,这个掩码可以在OnSerialize函数中通过syncVarDirtyBits访问到
  • Each SyncVar in a NetworkBehaviour script is assigned a bit in the dirty mask.
  • NetworkBehavious中的每个同步变量被指定了脏数据掩码中的一位
  • Changing the value of SyncVars causes the bit for that SyncVar to be set in the dirty mask
  • 对同步变量的修改会使对应的脏数据位被设置
  • Alternatively, calling SetDirtyBit() writes directly to the dirty mask
  • 或者可以通过调用SetDirtyBit函数直接修改脏数据标志位
  • NetworkIdentity objects are checked on the server as part of it’s update loop
  • 服务器的每个Update调用都会检查他的NetworkIdentity组件
  • If any NetworkBehaviours on a NetworkIdentity are dirty, then an UpdateVars packet is created for that object
  • 如果有标记为脏的NetworkBehaviour,就会为那个物体创建一个更新数据包
  • The UpdateVars packet is populated by calling OnSerialize on each NetworkBehaviour on the object
  • 每个NetworkBehaviour组件的OnSerialize函数都被调用,来构建这个更新数据包
  • NetworkBehaviours that are NOT dirty write a zero to the packet for their dirty bits
  • 没有脏数据位设置的NetworkBehaviour在数据包中添加0标志
  • NetworkBehaviours that are dirty write their dirty mask, then the values for the SyncVars that have changed
  • 有脏数据位设置的NetworkBehavious写入他们的脏数据和有改动的同步变量的值
  • If OnSerialize returns true for a NetworkBehaviour, the dirty mask is reset for that NetworkBehaviour, so it will not send again until it’s value changes.
  • 如果一个NetworkBehavious的OnSerialize函数返回了True,那么他的脏标志位被重置,因此直到下一次数据修改之前不会被再次发送
  • The UpdateVars packet is sent to ready clients that are observing the object
  • 更新数据包被发送到能看见这个物体的所有客户端

On the client:
在客户端上:
  • an UpdateVars packet is received for an object
  • 接收到一个物体的更新数据包
  • The OnDeserialize function is called for each NetworkBehaviour script on the object
  • 这个物体上的每个NetworkBehavious脚本的OnDeserialize函数被调用
  • Each NetworkBehaviour script on the object reads a dirty mask.
  • 这个物体上的每个NetworkBehavious脚本读取脏数据标识
  • If the dirty mask for a NetworkBehaviour is zero, the OnDeserialize functions returns without reading any more
  • 如果关联到这个NetworkBehaviour脚本的脏数据位是0,OnDeserialize函数直接返回;
  • If the dirty mask is non-zero value, then the OnDeserialize function reads the values for the SyncVars that correspond to the dirty bits that are set
  • 如果脏数据标志不是0,OnDeserialize函数继续读取后续的同步变量
  • If there are SyncVar hook functions, those are invoked with the value read from the stream.
  • 如果有同步变量的钩子函数,调用钩子函数

So for this script:
对下面的脚本:

[mw_shl_code=csharp,true]public class data : NetworkBehaviour
{

    [SyncVar]
    public int int1 = 66;

    [SyncVar]
    public int int2 = 23487;

    [SyncVar]
    public string MyString = "esfdsagsdfgsdgdsfg";
}[/mw_shl_code]

The generated OnSerialize function is something like:
产生的序列化函将如下所示:

[mw_shl_code=csharp,true]public override bool OnSerialize(NetworkWriter writer, bool forceAll)
{
    if (forceAll)
    {
        // the first time an object is sent to a client, send all the data (and no dirty bits)
        writer.WritePackedUInt32((uint)this.int1);
        writer.WritePackedUInt32((uint)this.int2);
        writer.Write(this.MyString);
        return true;
    }
    bool wroteSyncVar = false;
    if ((base.get_syncVarDirtyBits() & 1u) != 0u)
    {
        if (!wroteSyncVar)
        {
            // write dirty bits if this is the first SyncVar written
            writer.WritePackedUInt32(base.get_syncVarDirtyBits());
            wroteSyncVar = true;
        }
        writer.WritePackedUInt32((uint)this.int1);
    }
    if ((base.get_syncVarDirtyBits() & 2u) != 0u)
    {
        if (!wroteSyncVar)
        {
            // write dirty bits if this is the first SyncVar written
            writer.WritePackedUInt32(base.get_syncVarDirtyBits());
            wroteSyncVar = true;
        }
        writer.WritePackedUInt32((uint)this.int2);
    }
    if ((base.get_syncVarDirtyBits() & 4u) != 0u)
    {
        if (!wroteSyncVar)
        {
            // write dirty bits if this is the first SyncVar written
            writer.WritePackedUInt32(base.get_syncVarDirtyBits());
            wroteSyncVar = true;
        }
        writer.Write(this.MyString);
    }

    if (!wroteSyncVar)
    {
        // write zero dirty bits if no SyncVars were written
        writer.WritePackedUInt32(0);
    }
    return wroteSyncVar;
}[/mw_shl_code]

And the OnDeserialize function is something like:
反序列化函数将如下:

[mw_shl_code=csharp,true]public override void OnDeserialize(NetworkReader reader, bool initialState)
{
    if (initialState)
    {
        this.int1 = (int)reader.ReadPackedUInt32();
        this.int2 = (int)reader.ReadPackedUInt32();
        this.MyString = reader.ReadString();
        return;
    }
    int num = (int)reader.ReadPackedUInt32();
    if ((num & 1) != 0)
    {
        this.int1 = (int)reader.ReadPackedUInt32();
    }
    if ((num & 2) != 0)
    {
        this.int2 = (int)reader.ReadPackedUInt32();
    }
    if ((num & 4) != 0)
    {
        this.MyString = reader.ReadString();
    }
}[/mw_shl_code]

If a NetworkBehaviour has a base class that also has serialization functions, the base class functions should also be called.
如果这个NetworkBehaviour的基类也有一个序列化函数,基类的序列化函数也将被调用。

Note that the UpdateVar packets created for object state updates may be aggregated in buffers before being sent to the client, so a single transport layer packet may contain updates for multiple objects.
注意更新数据包可能会在缓冲区中合并,所以一个传输层数据包可能包含多个物体的更新数据包。

unity 5 multiplayer;api unity;unity 5.1 multiplayer;unity multiplayer;unity 有多少api;unity的api;unity api;unity中api什么意思
非常感谢。有中文看着轻松多了~{:107:}{:107:}


The OnDeserialize function is called for each NetworkBehaviour script on the object
“该物体上的”每个NetworkBehavious脚本的OnDeserialize函数被调用
引用: buzheteng 发表于 2016-2-2 22:36
非常感谢。有中文看着轻松多了~

谢谢给我指出来。