NetworkBehaviour は NetworkIdentity コンポーネントを持つオブジェクトとともに機能する、特殊なスクリプトです。このスクリプトでは、Commands、ClientRPCs、SyncEvents それに SyncVars などの HLAPI 機能を実行することができます。
Unity のネットワークシステムの1つであるサーバー権限を持つシステムでは、NetworkServer.Spawn() を使用してサーバー側で NetworkIdentity コンポーネントを持つネットワークオブジェクトを生成しなければいけません。NetworkInstanceId が割り当てられている場合、サーバーに接続されているすべてのクライアント上でネットワークオブジェクトが生成されます。
| プロパティー | 機能 | |
|---|---|---|
| isLocalPlayer | ローカルのクライアントで使用(操作)されるプレイヤーオブジェクトである場合は true を返します。 | |
| isServer | オブジェクトがサーバー上で実行されている場合は true | |
| isClient | オブジェクトがクライアント上で実行されている場合は true | |
| hasAuthority | オブジェクトがオリジナルの(権限を持つ)オブジェクトである場合、true を返します。これはサーバー側とクライアント側両方で使用します。ただし、クライアント側は localPlayerAuthority を使うことをおすすめします。 | |
| assetId | オブジェクトの NetworkIdentity のアセット ID です。 | |
| netId | オブジェクトの NetworkIdentity のネットワーク ID です。 | |
| playerControllerId | オブジェクトの NetworkIdentity の プレイヤーコントローラーID です。 | |
| connectionToServer | NetworkConnection オブジェクトをサーバーに送信するために使用します。 | |
| connectionToClient | NetworkConnection オブジェクトをクライアントに送信するために使用します。 | |
NetworkBehaviour は、以下の特徴を持っています。
NetworkBehaviour のメンバ変数は、サーバーからクライアントに同期させることができます。サーバーは、同期システムの権限を持っているので、サーバーからクライアントへの一方通行で同期されます。クライアントはコマンド機能を使って、同期権限のないクライアントからでもリクエストを行えます。
SyncVar 属性は、メンバ変数を同期するために使用されます。SyncVar は基本的な型が扱え、クラスやリスト、その他コレクションは扱えません。
public class SpaceShip : NetworkBehaviour
{
    [SyncVar]
    public int health;
    [SyncVar]
    public string playerName;
}
サーバー側で SyncVar の値が変更されたときゲーム内の準備ができているすべてのクライアントに向けて送信されます。オブジェクトが生成されたときは、サーバー側からすべての SyncVar で管理している最新のステータスを使ってクライアント側にオブジェクトを作成します。
さまざまなネットワークイベントを NetworkBehaviour 上で実行するためのコールバック関数があります。基本クラスの virtual 関数としてあるため、以下のようにオーバーライドして使用することができます。
public class SpaceShip : NetworkBehaviour
{
    public override void OnStartServer()
    {
        // クライアントに関するものを無効に
    }
    public override void OnStartClient()
    {
        // クライアントイベントを設定。エフェクトを有効にします
    }
}
OnStartServer 関数は、サーバー上でオブジェクトが生成されたとき、サーバーにあるシーン内のオブジェクトが起動した(読み込みした)ときに呼び出されます。OnStartClient 関数は、クライアント上でオブジェクトが生成されたとき、クライアントにあるシーン内のオブジェクトがサーバーと接続したときに呼び出されます。これらの関数は、サーバー上のみに影響をあたえる実装や、クライアント側のイベントを設定するときなど、クライアント/サーバー固有の実装を行うときに便利です。
ローカルクライアントを使用している場合は、OnStartServer と OnStartClient の両方の関数は同じオブジェクト上で呼び出されます。
他にもコールバックが用意されています。
NetworkBehaviour のメンバー関数は、サーバーのみ・クライアントのみで実行できる関数がカスタム属性を付けるだけで実装できます。
using UnityEngine;
using UnityEngine.Networking;
public class SimpleSpaceShip : NetworkBehaviour
{
    int health;
    [Server]
    public void TakeDamage( int amount)
    {
        // サーバーでのみ作動します
        health -= amount;
    }
    [Client]
    void ShowExplosion()
    {
        // クライアントでのみ実行します
    }
    [ClientCallback]
    void Update()
    {
        // エンジンがコールバックを発します- クライアントでのみ実行されます
    }
}
ServerとClientの属性の付いたメソッドは、クライアントやサーバーがアクティブではないときに呼び出されると、メソッド中の処理は実行されません。コンパイル時にエラーは出力されませんが、実行時にコンソールに警告ログとして出力されます。ServerCallback と ClientCallback 属性では、ユーザーのコードはその呼び出しを制御せずに、エンジン側のコールバック関数として使用することができます。つまり、これらの属性は警告ログを生成せずに使用できるということです。
コマンドは、サーバーに対してなんらかのリクエストを行うための方法です。HLAPI は、サーバー権威システムなので、クライアントはコマンドを通してのみ何かを行うことができます。コマンドは送信したクライアントに対応する、サーバー上のプレイヤーオブジェクトで実行されます。これは自動でルーティングが行われ、クライアントが別のプレイヤーに対して送信されるということはありません。
コマンドは、メソッド名にプレフィックスとして “Cmd” と、[Command] 属性を付ける必要があります。
using UnityEngine;
using UnityEngine.Networking;
public class SpaceShip : NetworkBehaviour
{
    bool alive;
    float thrusting;
    int spin;
    [Command]
    public void CmdThrust(float thrusting, int spin)
    {   
        if (!alive)
        {
            this.thrusting = 0;
            this.spin = 0;
            return;
        }
            
        this.thrusting = thrusting;
        this.spin = spin;
    }
    [ClientCallback]
    void Update()
    {
        int spin = 0;
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            spin += 1;
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            spin -= 1;
        }
        // this will be called on the server
        CmdThrust(Input.GetAxis("Vertical"), spin);
    }
}
コマンドは、クライアント上で通常通りにメソッドを呼び出すように実行します。しかし実際は、クライアント上でコマンドの関数が実行されているのではなく、サーバ上にあるクライアントのプレイヤーオブジェクトに対して実行されています。つまり、コマンドはタイプセーフで、組み込みのセキュリティーが扱え、プレイヤーをルーティングし、効率的なシリアライゼーションのメカニズムを引数によって、高速に呼び出すことができます。
クライアントの RPC 呼び出しは、サーバーで起こったことをクライアントのオブジェクトに伝えるための方法です。これはコマンド機能とは逆方向の機能ですが、考え方は同じです。RPC 呼び出しは、プレイヤーオブジェクト上で呼び出されるのではなく、NetworkIdentity オブジェクト上で呼び出されます。RPC 呼び出しは、メソッド名にプレフィックスとして “Rpc” と、[ClientRPC] 属性を付ける必要があります。
using UnityEngine;
using UnityEngine.Networking;
public class SpaceShipRpc : NetworkBehaviour
{
    [ClientRpc]
    public void RpcDoOnClient(int foo)
    {
        Debug.Log("OnClient " + foo);
    }
    [ServerCallback]
    void Update()
    {
        int value = UnityEngine.Random.Range(0,100);
        if (value < 10)
        {
            // これはすべてのクライアントで呼び出されます
            RpcDoOnClient(value);
        }
    }
}
ネットワークイベントは、クライアントの RPC 呼び出しのようなものです。ですが、違いとしてクライアント上のイベントとしてトリガーされた、クライアントオブジェクト上の関数が代わりに呼び出されます。イベントは、クライアント上で他のスクリプトからも登録することができ、サーバー上で実行することができます。イベントは、メソッド名にプレフィックスとして “Event” と、\[SyncEvent\] 属性を付ける必要があります。
イベントは他のスクリプトから拡張できる、強力なネットワークゲームのシステムを構築することができます。この例は、サーバー上にある Combat(戦闘)スクリプトによって生成されるイベントに、応答するクライアントのスクリプトを扱う方法です。
using UnityEngine;
using UnityEngine.Networking;
// サーバースクリプト
public class MyCombat : NetworkBehaviour
{
    public delegate void TakeDamageDelegate(int amount);
    public delegate void DieDelegate();
    public delegate void RespawnDelegate();
    
    float deathTimer;
    bool alive;
    int health;
    [SyncEvent(channel=1)]
    public event TakeDamageDelegate EventTakeDamage;
    
    [SyncEvent]
    public event DieDelegate EventDie;
    
    [SyncEvent]
    public event RespawnDelegate EventRespawn;
    [Server]
    void EventTakeDamage(int amount)
    {
        if (!alive)
            return;
            
        if (health > amount) {
            health -= amount;
        }
        else
        {
            health = 0;
            alive = false;
            //  die イベントをすべてのクライアントに送信
            EventDie();
            deathTimer = Time.time + 5.0f;
        }
    }
    [ServerCallback]
    void Update()
    {
        if (!alive)
        {
            if (Time.time > deathTimer)
            {
                Respawn();
            }
            return;
        }
    }
    [Server]
    void Respawn()
    {
        alive = true;
        //  respawn イベントをすべてのクライアントに送信
        EventRespawn();
    }
}