ネットワークシステムの概念
NetworkManager を使用する

0からマルチプレイヤープロジェクトをセットアップする

このドキュメントでは、新しいネットワーキング・システムを利用して新規のマルチプレイヤー・プロジェクトをゼロからセットアップする方法を説明しています。この手順は一般的なものですが、一度始めれば、さまざまな種類のマルチプレイヤーゲーム向けにカスタマイズ可能です。

まず始めに、空の Unity プロジェクトを新規で作成してください。

NetworkManager の設定

最初のステップは、プロジェクト内に NetworkManager オブジェクトを作成することです。

  • メニューの Game Object -> Create Empty から空のゲームオブジェクトを新規で追加します。
  • ヒエラルキービュー上で、新規作成したオブジェクトを見付け、選択してください。
  • オブジェクトの名前を “NetworkManager” に変更してください。これは、右クリックで表示されるコンテキストメニューでも行えますし、オブジェクトの名前をクリックして入力することも可能です。
  • オブジェクトのインスペクター・ウィンドウで、Add Component ボタンをクリックします。
  • コンポーネント Network -> NetworkManager を見付けてオブジェクトに追加してください。このコンポーネントがゲームのネットワーク状態を管理します。
  • Network -> NetworkManagerHUD コンポーネントを見付けてオブジェクトに追加してください。このコンポーネントは、ネットワーク状態を操作するための簡単なユーザーインターフェースをゲームに追加します。

詳しくは NetworkManager を使用する を参照してください。

Player Prefab の設定

次のステップは、ゲーム内でプレイヤーを表す Unity Prefab のセットアップです。デフォルトでは NetworkManager は、プレイヤーごとに1つ、オブジェクトのインスタンスを作成します。これは、プレイヤープレハブをコピーすることで行われます。この例では、プレイヤーオブジェクトは単純な立方体です。

  • メニューの Game Object -> 3D Object -> Cube から新規でキューブを作成してください。
  • 作成したキューブをヒエラルキービュー上で見付け、選択してください。
  • オブジェクトの名前を “PlayerCube” に変更します。
  • オブジェクトのインスペクターウィンドウで、Add Component ボタンをクリックしてください。
  • Network -> NetworkIdentity コンポーネントをオブジェクトに追加してください。このコンポーネントは、サーバーとクライアント間でオブジェクトを特定するのに使用されます。
  • NetworkIdentity の “Local Player Authority” のチェックボックスを True に設定してください。これにより、クライアントがプレイヤーオブジェクトの動きを制御できるようになります。
  • プレイヤーのキューブオブジェクトを Assets ウィンドウにドラッグすると、“PlayerCube” という名前のプレハブが作られます。
  • シーンから PlayerCube オブジェクトを削除してください。プレハブが作成されたので、もう必要ありません。

プレイヤーオブジェクト を参照してください。

プレイヤープレハブの登録

作成されたプレイヤープレハブは、ネットワークシステムに登録される必要があります。

  • ヒエラルキービューで NetworkManager オブジェクトを見付け、選択してください。
  • NetworkManager のインスペクターの折り畳みメニュー “Spawn Info” を開いてください。
  • “Player Prefab” のスロットを見付けてください。
  • PlayerCube プレハブを “Player Prefab” スロットにドラッグしてください。

ここまで来たらプロジェクトを保存しましょう。メニューを File -> Save Project と進み、プロジェクトを保存します。シーンも保存してください。このシーンを “offline” シーンと名付けましょう。

プレイヤーを動かす(シングルプレイヤー)

ゲーム機能の開発でまず最初に行うのは、プレイヤーオブジェクトを動かすことです。始めはネットワークを使わずに行うので、シングルプレイヤーモードでのみ機能します。

  • アセットビューで PlayerCube プレハブを見付けてください。
  • Add Component ボタンをクリックし、“New Script” を選択します。
  • スクリプト名として “PlayerMove” と入力してください。新規スクリプトが作成されます。
  • この新規スクリプトをエディター(Visual Studio など)で開きます。ダブルクリックで開けます。
  • 以下の簡単な動きのコードをスクリプトに追加してください。
using UnityEngine;

public class PlayerMove : MonoBehaviour
{
    void Update()
    {
        var x = Input.GetAxis("Horizontal")*0.1f;
        var z = Input.GetAxis("Vertical")*0.1f;

        transform.Translate(x, 0, z);
    }
}

これでキューブが矢印キーやタッチパッドで制御できるようになります。この時点ではキューブはクライアント上でのみ動きます ― ネットワーク化されていない状態です。

プロジェクトを再度保存してください。

ホストされたゲームをテストする

Play ボタンをクリックするとエディターがプレイモードに入ります。NetworkManagerHUD デフォルト ユーザー インターフェースが表示されます。

“Host” を押すと、ゲームのホストとしてゲームが開始されます。プレイヤーオブジェクトが作成され、HUD はサーバーがアクティブであることを示す状態に変化します。このゲームは、サーバーであると同時にクライアントでもある、「ホスト」として実行されます。

ネットワークシステムの概念 を参照してください。

方向キーを押すとプレイヤー キューブ オブジェクトが動きます。

エディターで Stop ボタンを押すとプレイモードが終了します。

クライアント上でプレイヤーの動きをテストする

  • メニューを File -> Build Settings と進み、Build Settings ダイアログを開きます。
  • “Add Open Scenes” ボタンを押すと、現在のシーンがビルドに追加されます。
  • “Build and Run” ボタンを押してビルドを作成してください。実行ファイルの名前設定のプロンプトが表示されますので、名前(例えば “networkTest” など)を入力してください。
  • スタンドアローンのプレイヤーが起動され、解像度選択のダイアログが表示されます。
  • “windowed” のチェックボックスをオンにし、低い解像度(640x480 など)を選択してください。
  • スタンドアローンのプレイヤーが開始され、NetworkManager HUD が表示されます。
  • メニューから “Host” を選択するとホストとして開始できます。プレイヤーキューブが1つ作成されます。
  • 方向キーを押して、プレイヤーキューブを少し動かしてみてください。
  • エディターに戻り、Build Settings ダイアログを閉じてください。
  • Play ボタンを押してプレイモードを開始してください。
  • NetworkManagerHUD ユーザー インターフェースで “LAN Client” を選択すると、クライアントとしてホストに接続されます。
  • キューブは2つ存在しているはずです。ひとつはホスト上のローカルプレイヤー用、もうひとつはこのクライアント上のリモートプレイヤー用です。
  • 方向キーを押してプレイヤーキューブを動かしてください。
  • 2つのキューブが両方動くようになりました。これは、動きのスクリプトがネットワークを認識していないためです。

プレイヤーの動きをネットワーク化する

  • スタンドアローン プレイヤーを閉じてください。
  • エディターでプレイモードを終了します。
  • PlayerMove スクリプトを開いてください。
  • スクリプトを、ローカルプレイヤーだけが動くように更新してください。
  • “using UnityEngine.Networking” を追加します。
  • “MonoBehaviour” を “NetworkBehaviour” に変更します。
  • Update 関数内で、“isLocalPlayer” にチェックを加えます。こうすることで、ローカルプレイヤーのみが入力を処理するようになります。
using UnityEngine;
using UnityEngine.Networking;

public class PlayerMove : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal")*0.1f;
        var z = Input.GetAxis("Vertical")*0.1f;

        transform.Translate(x, 0, z);
    }
}
  • アセットビューで PlayerCube プレハブを見付け、選択してください。
  • “Add Component” ボタンをクリックし、Networking -> NetworkTransform コンポーネントを追加します。このコンポーネントによって、ネットワーク上でオブジェクトの位置が同期されるようになります。
  • プロジェクトを再度保存してください。

マルチプレイヤーの動きをテストする

  • スタンドアローン・プレイヤーのビルドと実行を再度行い、ホストとして開始してください。
  • エディターでプレイモードを開始し、クライアントとして接続してください。
  • これで、各プレイヤーオブジェクトがそれぞれのクライアントのローカルプレイヤーによって制御されて、別々に動くようになりました。

プレイヤーの識別

この時点では、ゲーム内のプレイヤーはすべて白色なため、ユーザーはどれが自分のキューブなのか見分けることができません。プレイヤーを識別できるようにするために、ローカルプレイヤーのキューブを赤色にしてみましょう。

  • PlayerMove スクリプトを開きます。
  • プレイヤーオブジェクトの色を変えるために、OnStartLocalPlayer 関数の実装を追加します。
    public override void OnStartLocalPlayer()
        {
            GetComponent<MeshRenderer>().material.color = Color.red;
        }

この関数は、ローカルプレイヤーにのみ、そのクライアント上で呼び出されます。この関数によって、ユーザー自身のキューブが赤く表示されます。OnStartLocalPlayer 関数は、ローカルプレイヤーのみの初期化を行うのに適した場所です。(例えばカメラと入力の設定など。)

NetworkBehaviour のベースクラスには、便利な仮想関数が他にもあります。オブジェクトの Spawn(生成) を参照してください。

  • ゲームのビルドと実行を行ってください。
  • ローカルプレイヤーが操作するキューブが赤色になりました。その他のキューブは白色のままです。

弾の発射(非ネットワーク)

マルチプレイヤーゲームで頻繁に使用される機能のひとつに、弾の発射があります。このセクションでは、参考例に非ネットワークの弾を追加していきます。弾のネットワーク化に関しては、次のセクションで説明します。

  • 球体(Sphere)のゲームオブジェクトを作成します。
  • 作成した球体オブジェクトの名前を “Bullet” に変更します。
  • 弾のスケールを 1.o から 0.2 に変更してください。
  • 弾を Assets フォルダーにドラッグして弾のプレハブを作成してください。
  • シーンから弾のオブジェクトを削除します。
  • リジッドボディ コンポーネントを弾に追加します。
  • リジッドボディの “Use Gravity” チェックボックスを False に設定してください。
  • 弾が発射されるように PlayerMove スクリプトを更新してください。
  • 弾のプレハブ用に public スロットを追加してください。
  • Update() 関数に入力処理を追加してください。
  • 弾を発射する関数を追加してください。
using UnityEngine;
using UnityEngine.Networking;

public class PlayerMove : NetworkBehaviour
{
    public GameObject bulletPrefab;

    public override void OnStartLocalPlayer()
    {
        GetComponent<MeshRenderer>().material.color = Color.red;
    }

    void Update()
    {
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal")*0.1f;
        var z = Input.GetAxis("Vertical")*0.1f;

        transform.Translate(x, 0, z);

        if (Input.GetKeyDown(KeyCode.Space))
        {
            Fire();
        }
    }

    void Fire()
    {
        // 弾丸のプレハブから弾丸のオブジェクトを作成
        var bullet = (GameObject)Instantiate(
            bulletPrefab,
            transform.position - transform.forward,
            Quaternion.identity);

        // プレイヤーの前から弾丸を移動する
        bullet.GetComponent<Rigidbody>().velocity = -transform.forward*4;
        
        // 2 秒後に弾丸を消す
        Destroy(bullet, 2.0f);        
    }
}
  • スクリプトを保存し、エディターに戻ってください。
  • PlayerCube プレハブを選択し、PlayerMove コンポーネントを見付けてください。
  • コンポーネントの bulletPrefab スロットを見付けてください。
  • 弾のプレハブを bulletPrefab のスロットにドラッグしてください。
  • ビルドを作成し、ホストとしてスタンドアローンのプレイヤーを開始してください。
  • エディターでプレイモードを開始し、クライアントとして接続してください。
  • スペースバーを押すと弾が生成されてプレイヤーオブジェクトがら発射されます。
  • 他のクライアント上では弾は発射されません。スペースバーが押されたクライアント上でのみ発射されます。

弾の発射(ネットワーク使用)

このセクションでは、参考例の弾にネットワークを追加していきます。

  • 弾のプレハブを見付け、選択してください。
  • 弾のプレハブに NetworkIdentity を追加してください。
  • 弾のプレハブに NetworkTransform コンポーネントを追加してください。
  • 弾のプレハブの NetworkTransform コンポーネントの Send Rate を 0 に設定してください。弾の発射された後の方向や速度は変わらないので、動き(Movement)の更新を送る必要はありません。
  • NetworkManager を選択し、“Spawn Info” の折り畳みメニューを開いてください。
  • 「+」ボタンで、新規で Spawn(オブジェクト生成)プレハブを追加してください。
  • 新規作成した Spawn(オブジェクト生成)プレハブのスロットに、弾のプレハブをドラッグしてください。
  • PlayerMove スクリプトを開きます。
  • PlayerMove スクリプトを更新して弾をネットワーク化します。
  • カスタム属性 [Command] と 接頭辞 “Cmd” を追加して、発射の機能をネットワークコマンドに変更してください。
  • 弾のオブジェクトに Network.Spawn() を使用してください。
using UnityEngine;
using UnityEngine.Networking;

public class PlayerMove : NetworkBehaviour
{
    public GameObject bulletPrefab;
    
    public override void OnStartLocalPlayer()
    {
        GetComponent<MeshRenderer>().material.color = Color.red;
    }

    [Command]
    void CmdFire()
    {
       //  [Command] はサーバー上で実行

       // 弾丸オブジェクトをローカルに作成
       var bullet = (GameObject)Instantiate(
            bulletPrefab,
            transform.position - transform.forward,
            Quaternion.identity);

       bullet.GetComponent<Rigidbody>().velocity = -transform.forward*4;
       
       //クライアント上に弾丸を生成
       NetworkServer.Spawn(bullet);
       
       // サーバー上で弾丸が破棄されると、自動的にクライアント上で破棄されます
       Destroy(bullet, 2.0f);
    }

    void Update()
    {
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal")*0.1f;
        var z = Input.GetAxis("Vertical")*0.1f;

        transform.Translate(x, 0, z);

        if (Input.GetKeyDown(KeyCode.Space))
        {
            // コマンド関数はクライアントから呼び出されますが、サーバー上で実行されます。
            CmdFire();
        }
    }
}

このコードは、サーバー上で弾を発射するのに [Command] を使用しています。詳しくは リモートアクション をご参照ください。

  • ビルドを作成し、ホストとしてスタンドアローンのプレイヤーを開始してください。
  • エディターでプレイモードを開始し、クライアントとして接続してください。
  • スペースバーを押すと、すべてのクライアント上の正しいプレイヤー(のみ)で、弾が発射されます。

弾の衝突

衝突処理を追加して、弾がプレイヤーキューブオブジェクトに当たったときに消えるようにします。

  • 弾のプレハブを見付け、選択してください。
  • Add Component ボタンを選択して新規スクリプトを追加してください。
  • 新規スクリプト “Bullet” を呼び出します。
  • 上記で追加した新規スクリプトを開き、プレイヤーオブジェクトに当たった弾を破壊する衝突処理を追加します。
using UnityEngine;

public class Bullet : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        var hit = collision.gameObject;
        var hitPlayer = hit.GetComponent<PlayerMove>();
        if (hitPlayer != null)
        {
            Destroy(gameObject);
        }
    }
}

これで、プレイヤーオブジェクトに当たった弾が破壊されるようになりました。弾はネットワークで管理された生成オブジェクトなので、サーバー上で破壊されるとクライアント上でも破壊されます。

プレイヤーのステート(非ネットワークの体力値)

弾と関連してもうひとつ一般的な機能は、プレイヤーオブジェクトの「体力」(Health/ヒットポイント)プロパティーです。これは満タンな状態でスタートし、プレイヤーが弾に当たってダメージを受けると低下します。このセクションでは、プレイヤーオブジェクトに非ネットワークの「体力」プロパティーを追加します。

  • PlayerCube プレハブを選択します。
  • Add Component ボタンを選択して新規スクリプトを追加してください。
  • “Combat” スクリプトを呼び出します。
  • Combat スクリプトを開き、体力の変数と TakeDamage 関数を追加してください。
using UnityEngine;

public class Combat : MonoBehaviour 
{
    public const int maxHealth = 100;
    public int health = maxHealth;

    public void TakeDamage(int amount)
    {
        health -= amount;
        if (health <= 0)
        {
            health = 0;
            Debug.Log("Dead!");
        }
    }
}

弾が当たったときに TakeDamage スクリプトを呼び出すには、弾のスクリプトが更新される必要があります。 * 弾(Bullet)のスクリプトを開きます。 * 衝突処理関数内で Combat スクリプトから TakeDamage() への呼び出しを追加してください。

using UnityEngine;

public class Bullet : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        var hit = collision.gameObject;
        var hitPlayer = hit.GetComponent<PlayerMove>();
        if (hitPlayer != null)
        {
            var combat = hit.GetComponent<Combat>();
            combat.TakeDamage(10);

            Destroy(gameObject);
        }
    }
}

これで、弾に当たったときにプレイヤーオブジェクトの体力が減るようになりました。ただし、これはゲーム内では確認することができません。簡単な体力ゲージを追加する必要があります。 * PlayerCube プレハブを選択します。 * Add Component ボタンを選択し、HealthBar という名前の新規スクリプトを追加してください。 * HealthBar スクリプトを開きます。

下記の長いコードは、古い GUI システムを使用したものです。これを使用しますが、ネットワーキングとはあまり関係がないので、ここでは説明は省略します。

using UnityEngine;
using System.Collections;

public class HealthBar : MonoBehaviour 
{
    GUIStyle healthStyle;
    GUIStyle backStyle;
    Combat combat;

    void Awake()
    {
        combat = GetComponent<Combat>();
    }

    void OnGUI()
    {
        InitStyles();

        // Health Bar を描画

        Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
        
        // Health Bar の背景を描画
        GUI.color = Color.grey;
        GUI.backgroundColor = Color.grey;
        GUI.Box(new Rect(pos.x-26, Screen.height - pos.y + 20, Combat.maxHealth/2, 7), ".", backStyle);
        
        // Health Bar に体力値を描画
        GUI.color = Color.green;
        GUI.backgroundColor = Color.green;
        GUI.Box(new Rect(pos.x-25, Screen.height - pos.y + 21, combat.health/2, 5), ".", healthStyle);
    }

    void InitStyles()
    {
        if( healthStyle == null )
        {
            healthStyle = new GUIStyle( GUI.skin.box );
            healthStyle.normal.background = MakeTex( 2, 2, new Color( 0f, 1f, 0f, 1.0f ) );
        }

        if( backStyle == null )
        {
            backStyle = new GUIStyle( GUI.skin.box );
            backStyle.normal.background = MakeTex( 2, 2, new Color( 0f, 0f, 0f, 1.0f ) );
        }
    }
    
    Texture2D MakeTex( int width, int height, Color col )
    {
        Color[] pix = new Color[width * height];
        for( int i = 0; i < pix.Length; ++i )
        {
            pix[ i ] = col;
        }
        Texture2D result = new Texture2D( width, height );
        result.SetPixels( pix );
        result.Apply();
        return result;
    }
}
  • プロジェクトを保存してください。
  • ゲームのビルドと実行(Build and Run)を行い、プレイヤーオブジェクトの体力ゲージを確認してください。
  • プレイヤーが他のプレイヤーを撃つと、撃たれたプレイヤーのクライアント上で体力が低下します。その他のクライアント上では低下しません。

プレイヤーのステート(体力値のネットワーク化)

この時点で、体力の変化はクライアントとホストそれぞれに独立して適用されるようになっています。つまりプレイヤーごとに異なる体力値を持てるということです。しかし、体力値はサーバー上のみで適用されて、その変化がクライアントに反映されるという形にする必要があります。これは、体力の「サーバー権限」と呼ばれます。

  • Combat スクリプトを開いてください。
  • スクリプトを NetworkBehaviour に変更します。
  • 体力を [SyncVar] にします。
  • TakeDamage に isServer チェックを追加し、サーバーでのみ適用されるようにします。

SyncVar に関する詳細は、ステートの同期 を参照してください。

using UnityEngine;
using UnityEngine.Networking;

public class Combat :  NetworkBehaviour 
{
    public const int maxHealth = 100;

    [SyncVar]
    public int health = maxHealth;

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

        health -= amount;
        if (health <= 0)
        {
            health = 0;
            Debug.Log("Dead!");
        }
    }
}

死と再生成

この時点では、プレイヤーの体力がゼロになっても、ログメッセージが表示される以外は何も起こりません。よりゲームらしくするために、体力がゼロになったらプレイヤーがスタート地点に戻されて体力が満タンに戻るようにしましょう。

  • Combat スクリプトを開いてください。
  • プレイヤーオブジェクトを再生成するための [ClientRpc] 関数を追加します。詳しくは リモートアクション を参照してください。
  • 体力がゼロに達したらサーバー上で再生成の関数を呼び出します。
using UnityEngine;
using UnityEngine.Networking;

public class Combat :  NetworkBehaviour 
{
    public const int maxHealth = 100;

    [SyncVar]
    public int health = maxHealth;

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

        health -= amount;
        if (health <= 0)
        {
            health = maxHealth;

            // サーバー上で呼び出され、クライアント上で実行されます
            RpcRespawn();
        }
    }

    [ClientRpc]
    void RpcRespawn()
    {
        if (isLocalPlayer)
        {
            // 0 の位置に戻ります
            transform.position = Vector3.zero;
        }
    }
}

このゲームでは、プレイヤーオブジェクトの位置がクライアントによって制御されています。プレイヤーオブジェクトはクライアント上で「ローカル権限」を持っています。もしプレイヤーの位置が直接サーバーによってスタート位置に設定されたとしたら、クライアントがそれをオーバーライドしてしまいます。クライアントが権限を持っているからです。これを回避するために、サーバーから権限を持つクライアントに対して、プレイヤーをスタート位置に移動するように命令が送られるようになっています。

  • ゲームのビルドと実行を行ってください。
  • プレイヤーオブジェクトをスタート位置から遠くに動かします。
  • 特定のプレイヤーを、体力がゼロになるまで弾で撃ちます。
  • 撃たれたプレイヤーオブジェクトは、スタート位置に戻されます。

ノンプレイヤー オブジェクト

クライアントがホストに接続するとプレイヤーオブジェクトが生成さますが、ほとんどのゲームでは、ゲーム世界の中に敵などのノンプレイヤー・オブジェクトが存在しています。このセクションでは、射撃したり倒したりすることのできるノンプレイヤー・オブジェクトの Spawner(生成機)を追加していきます。

  • GameObject メニューから、空のゲームオブジェクトを新規作成します。
  • このオブジェクトの名前を “EnemySpawner” に変更します。
  • EnemySpawner オブジェクトを選択します。
  • Add Component ボタンを選択し、オブジェクトに NetworkIdentity を追加します。
  • NetworkIdentity 内で “Server Only” のチェックボックスをクリックします。これによって、Spawner がクライアントに送信されなくなります。
  • Add Component ボタンを選択し、“EnemySpawner” という名前の新規スクリプトを作成してください。
  • 新規スクリプトを編集してください。
  • そのスクリプトを NetworkBehaviour にしてください。
  • 敵を生成するための仮想関数 OnStartServer を実装してください。
using UnityEngine;
using UnityEngine.Networking;

public class EnemySpawner : NetworkBehaviour {

    public GameObject enemyPrefab;
    public int numEnemies;

    public override void OnStartServer()
    {
        for (int i=0; i < numEnemies; i++)
        {
            var pos = new Vector3(
                Random.Range(-8.0f, 8.0f),
                0.2f,
                Random.Range(-8.0f, 8.0f)
                );

            var rotation = Quaternion.Euler( Random.Range(0,180), Random.Range(0,180), Random.Range(0,180));

            var enemy = (GameObject)Instantiate(enemyPrefab, pos, rotation);
            NetworkServer.Spawn(enemy);
        }
    }
}

上記が完了したら、Enemy のプレハブを作成してください。

  • GameObject メニューから、Capsule を新規作成してください。
  • オブジェクトの名前を “Enemy” に変更してください。
  • Add Component ボタンを選択し、Enemy に NetworkIdentity コンポーネントを追加してください。
  • Add Component ボタンを選択し、Enemy に NetworkTransform コンポーネントを追加してください。
  • Enemy オブジェクトをアセットビューにドラッグしてプレハブを作成してください。
  • “Enemy” という名前のプレハブアセットが作成されました。
  • シーンから Enemy オブジェクトを削除してください。
  • Enemy のプレハブを選択してください。
  • Add Component ボタンを選択し、Enemy に Combat スクリプトを追加してください。
  • Add Component ボタンを選択し、Enemy に HealthBar スクリプトを追加してください。
  • NetworkManager を選択し、Spawn Info 内に生成可能なプレハブを新規で1つ追加してください。
  • 作成した新規の Spawn プレハブを Enemy プレハブに設定します。

弾のスクリプトは、プレイヤーに対してのみ機能するように設定されています。これを、Combat スクリプトを持つすべてのオブジェクトに対して機能するように更新しましょう。

  • 弾(Bullet)のスクリプトを開きます。
  • 衝突チェックを、PlayerMove ではなく Combat を使用するように変更します。
using UnityEngine;

public class Bullet : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        var hit = collision.gameObject;
        var hitCombat = hit.GetComponent<Combat>();
        if (hitCombat != null)
        {
            hitCombat.TakeDamage(10);
            Destroy(gameObject);
        }
    }
}

EnemySpawner を Enemy オブジェクトと関連付けます。

  • EnemySpawner オブジェクトを選択します。
  • EnemySpawner コンポーネントで “Enemy” スロットを見付けてください。
  • Enemy のプレハブをスロットにドラッグしてください。
  • numEnemies の値を 4 に設定してください。

敵のテストを行います。

  • ゲームのビルドと実行を行ってください。
  • ホストとして開始すると、4体の敵がランダムな場所に生成されます。
  • プレイヤーは敵を射撃することができ、撃たれた敵の体力は低下します。
  • クライアントがゲームに参加すると、敵はそのクライアント上でもサーバー上でも同じ位置と体力値で表示されます。

敵の破壊

敵もプレイヤー同様に、弾に撃たれて体力が低下し、再生成されることも可能ですが、敵の場合は体力がゼロになると再生成の代わりに破壊されるのが通常です。

  • Combat スクリプトを開いてください。
  • “destroyOnDeath” 変数を1つ追加します。
  • 体力がゼロに達したら destroyOnDeath をチェックします。
using UnityEngine;
using UnityEngine.Networking;

public class Combat :  NetworkBehaviour 
{
    public const int maxHealth = 100;
    public bool destroyOnDeath;

    [SyncVar]
    public int health = maxHealth;

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

        health -= amount;
        if (health <= 0)
        {
            if (destroyOnDeath)
            {
                Destroy(gameObject);
            }
            else
            {
                health = maxHealth;

                // サーバー上で呼び出され、クライアント上で実行されます
                RpcRespawn();
            }
        }
    }

    [ClientRpc]
    void RpcRespawn()
    {
        if (isLocalPlayer)
        {
            // 0 の位置へ戻ります
            transform.position = Vector3.zero;
        }
    }
}

  • Enemy のプレハブを選択してください。
  • 敵の destroOnDeath チェックボックスを True に設定します。

これで、敵の体力がゼロになると破壊されるようになりました。プレイヤーの場合は再生成されます。

プレイヤーの生成位置

この時点では、すべてのプレイヤーがゼロ地点に生成されます。これではプレイヤー同士が重なってしまうことがありえます。したがって、プレイヤーは異なる位置に生成されるほうが好ましいと言えます。NetworkStartPosition コンポーネントを使ってこの設定を行ってみましょう。

  • 空の GameObject を新規で作成します。

  • 作成したオブジェクトの名前を “Pos1” に変更します。

  • Add Component ボタンを選択し、NetworkStartPosition コンポーネントを追加してください。

  • Pos1 オブジェクトを (–3,0,0) の位置に移動してください。

  • 空の GameObject をもう1つ作成してください。

  • 作成したオブジェクトの名前を “Pos2” に変更してください。

  • Add Component ボタンを選択し、NetworkStartPosition コンポーネントを追加してください。

  • Pos2 オブジェクトを (3,0,0) の位置に移動してください。

  • NetworkManager を見付けて選択してください。

  • “Spawn Info” の折り畳みメニューを開きます。

  • “Player Spawn Method” を “Round Robin” に変更してください。

  • ゲームのビルドと実行を行ってください。

  • プレイヤーオブジェクトが、ゼロ地点ではなく Pos1 オブジェクトと Pos2 オブジェクトの位置に作成されるようになりました。

ネットワークシステムの概念
NetworkManager を使用する