注意 UNet は非推奨となり、今後 Unity から削除される予定です。新しいシステムが開発中です。詳細は ブログ と FAQ を参照してください。 |
大抵の場合は、ゲームスクリプトがその状態をクライアント向けにシリアライズするには SyncVars を使用するだけで十分です。ただし、より複雑なシリアル化のコードが必要な場合もあります。このページは、Unity の通常の SyncVar 機能を超えるカスタムの同期ソリューションを必要とする、より精通した開発者を対象としています。
カスタムのシリアル化を実行するには、NetworkBehaviour に仮想関数を実装してSyncVar のシリアル化に使用します。これらの機能は以下の通りです。
public virtual bool OnSerialize(NetworkWriter writer, bool initialState);
public virtual void OnDeSerialize(NetworkReader reader, bool initialState);
initialState
フラグ は、オブジェクトが初めてシリアライズされる時とその後、インクリメントな更新が送信される時とを区別するのに便利です。オブジェクトが初めてクライアントに送られる時は、完全な状態のスナップショットが含まれている必要がありますが、その後の更新ではその度毎の変更分のみが含まれていれば良いので、情報処理量を節約できます。SyncVar フック関数は増分更新の為にだけ呼び出され、initialState
が True のときには呼び出されません。
クラスに SyncVar がある場合、これらの関数はそのクラスに自動的に加えられます。そのため、SyncVar を持つクラスは同時にカスタムのシリアル化の関数を持つことはできません。
OnSerialize
関数は、更新が送信されなければならないことを示すために True を返します。True が返されるとそのスクリプト用のダーティビットは 0 に設定されます。もし、False が返されるとダーティビットは変更されません。これを利用すると、変更を毎フレーム送信するのではなく、時間の経過とともに蓄積した複数のスクリプトへの変更を、システムの準備が整ったときに送信することが可能になります。
NetworkIdentity コンポーネントを持ったゲームオブジェクトは NetworkBehaviour から派生するスクリプトを複数持つことができます。オブジェクトのシリアライズの流れは以下の通りです。
サーバー上
各 NetworkBehaviour
にダーティマスクがあります。このマスクは OnSerialize
の中でsyncVarDirtyBits
として使用可能です。
NetworkBehaviour
スクリプト内のそれぞれの SyncVar にダーティマスクの中のビットが 1 つ割り当てられます。
SyncVar の値が変更されると、その SyncVar のビットがダーティマスクで設定されます。
または、SetDirtyBit()
の呼び出しによって、ビットが直接ダーティマスクに書き込まれます。
NetworkIdentity ゲームオブジェクトが、その更新ループの 1 部としてサーバーで確認されます。
NetworkIdentity
の NetworkBehaviours
がダーティな場合、そのゲームオブジェクトのために UpdateVars
パケットが作成されます。
OnSerialize
を呼び出すことによって、UpdateVars
パケットがゲームオブジェクトの各 NetworkBehaviour
に加えられます。
ダーティでない NetworkBehaviour
は、そのダーティビットのパケットに 0 を記述します。
ダーティな NetworkBehaviour
はそのダーティマスクに書き込み、その後、変更された SyncVars の値を書き込みます。
OnSerialize
が NetworkBehaviour
に対して True を返すと、その NetworkBehaviour
ダーティマスクはリセットされます。そのため、その値が変更されるまではパケットは再度送信されません。
UpdateVars
パケットが、ゲームオブジェクトを監視しているクライアントで準備の整ったものに送信されます。
クライアント上
オブジェクトが UpdateVars packet
を受け取ります。
OnDeserialize
関数がゲームオブジェクトの各 NetworkBehaviour
スクリプトで呼び出されます。
各オブジェクトの NetworkBehaviour
スクリプトがダーティマスクを読み込みます。
NetworkBehaviour
のダーティマスクが 0 の場合は、OnDeserialize
関数は、それ以上読み込むことなくリターンします。
ダーティマスクが 0 以外の値のときは、OnDeserialize
関数は、設定されたダーティビットに対応する SyncVar の値を読み込みます。
SyncVar フック関数がある場合、ストリームから読み込まれた値で実行されます。
したがって、このスクリプトの場合
public class data : NetworkBehaviour
{
[SyncVar]
public int int1 = 66;
[SyncVar]
public int int2 = 23487;
[SyncVar]
public string MyString = "Example string";
}
以下のコード例は、生成された OnSerialize
関数を示しています。
public override bool OnSerialize(NetworkWriter writer, bool forceAll)
{
if (forceAll)
{
// 初めてオブジェクトをクライアントに送信。すべてのデータを送ります (ダーティビットは含みません)
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)
{
//これが、初めての SyncVar の書き込みである場合は、ダーティビットを書き込みます
writer.WritePackedUInt32(base.get_syncVarDirtyBits());
wroteSyncVar = true;
}
writer.WritePackedUInt32((uint)this.int1);
}
if ((base.get_syncVarDirtyBits() & 2u) != 0u)
{
if (!wroteSyncVar)
{
// これが、初めての SyncVar の書き込みである場合は、ダーティビットを書き込みます
writer.WritePackedUInt32(base.get_syncVarDirtyBits());
wroteSyncVar = true;
}
writer.WritePackedUInt32((uint)this.int2);
}
if ((base.get_syncVarDirtyBits() & 4u) != 0u)
{
if (!wroteSyncVar)
{
// これが、初めての SyncVar の書き込みである場合は、ダーティビットを書き込みます
writer.WritePackedUInt32(base.get_syncVarDirtyBits());
wroteSyncVar = true;
}
writer.Write(this.MyString);
}
if (!wroteSyncVar)
{
// SyncVar の書き込みがない場合は、ダーティビットに 0 を書き込みます
writer.WritePackedUInt32(0);
}
return wroteSyncVar;
}
以下のコード例は OnDeserialize
関数を示しています。
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();
}
}
もし NetworkBehaviour
に基本クラスがあり、それがシリアル化関数も持っている場合、その基本クラスの関数も呼び出されます。
オブジェクトの状態更新用に作成されたUpdateVar
パケットはクライアントに送信される前にバッファーに集められるため、1 つのトランスポート層パケットに複数のオブジェクトの更新が含まれることがあります。