敵NPCを各種行動処理をホストが実行する

目次

  概要
  敵キャラクタを操作している箇所を探す
  NPC_PatrolにMonobitEngine.MonobitNetwork.isHostを適用する
  NPC_ShootPlayerにも同様の処置を適用する
  NPC_DamageReceiverにも同様の処置を適用する
  敵死亡時の同期制御


概要

敵の行動処理についてもホストが実行するように変更を加える

  前述の 敵NPCの出現処理 の続きです。
  敵の行動処理についてもホストが実行するように順次変更を加えていきましょう。


敵キャラクタを操作している箇所を探す

敵キャラクタのプレハブから「Bear」を選択する

  敵キャラクタの各種行動処理は敵キャラクタのプレハブ内にコンポーネントとして追加されています。
  そのコンポーネントが含まれている「Bear」オブジェクトを選択しましょう。

  この箇所は Unity 2018.2 以前と Unity2018.3 以降でインタフェースが異なりますので、
  お使いの Unity のバージョン に合わせて、以下の項目を選択して進めてください。

 Assets/Resources フォルダを開き、 NPC.prefab の右端にある「>」のマークをクリックします。
  すると、NPC.prefab の中身が展開されます。
  展開されたデータのうち、「Bear」と書かれたオブジェクトを選択します。



  まず Assets/Resources フォルダーをクリックして選択し、その中にある NPC.prefab を選択します。
  NPC の Inspector にある「Open Prefab」のボタンを押します。
  すると Hierarchy の表示箇所に NPC.prefab がリスト化されて表示されますので、
  親オブジェクト「NPC」の左隣にある三角マークをクリックし、子オブジェクトの「Bear」を選択します。


NPC_PatrolにMonobitEngine.MonobitNetwork.isHostを適用する

「Bear」のInspectorから「NPC_Patrol」を開く

  プレイヤーキャラクタのDudeと同様に、敵キャラクタの「Bear」にも、
  敵キャラクタ制御に関する様々なコンポーネントが登録されています。

  まずはこの中の、敵キャラクタの移動制御が含まれている NPC_Patrol コンポーネントの
  スクリプトファイルをダブルクリックして開いてみましょう。

該当するクラスの Start() について、Spawner.Update() で削除した処理の代替を組み込む

  ここで先述の Spawner.cs 内の Spawner.Update() メソッドで削除した処理の代替を書き加えます。
  NPC_Patrol.cs の 28 行目付近にある Start() メソッドに対し、以下のコードを追記してください。
		transform.parent.parent  = GameObject.Find("NPC_Spawner").transform.GetChild(0).transform;
		gameObject.SetActive(true);
  実行タイミングが異なりますが、これで処理内容が合致します。

該当するクラスの Update() について、MonobitEngine.MonobitNetwork.isHost による実行可否判定を入れる

  更に、NPC_Patrol.cs の 37 行目付近から、以下のコードを記述します。
		// ホスト以外は処理をしない
		if( !MonobitEngine.MonobitNetwork.isHost )
		{
			return;
		}
  「ホスト権限を持たないクライアントでは、Update() を処理させない」というロジックを適用させます。


NPC_ShootPlayerにも同様の処置を適用する

「Bear」のInspectorから「NPC_ShootPlayer」を開く

  同様の処置を他のスクリプトにも適用させます。

  続けて、敵キャラクタの攻撃処理が含まれている NPC_ShootPlayer コンポーネントの
  スクリプトファイルをダブルクリックして開いてみましょう。

該当するクラスの Update() について、MonobitEngine.MonobitNetwork.isHost による実行可否判定を入れる

  NPC_ShootPlayer.cs の 36 行目付近にある Update() メソッドに、以下のコードを追記します。
		// ホスト以外は処理をしない
		if( !MonobitEngine.MonobitNetwork.isHost )
		{
			return;
		}
  「ホスト権限を持たないクライアントでは、Update() を処理させない」というロジックを適用させます。


NPC_DamageReceiverにも同様の処置を適用する

「Bear」のInspectorから「NPC_DamageReceiver」を開く

  同様に、敵キャラクタのダメージアクション制御が含まれている NPC_DamageReceiver コンポーネントの
  スクリプトファイルをダブルクリックして開いてみましょう。

該当するクラスの Update() について、MonobitEngine.MonobitNetwork.isHost による実行可否判定を入れる

  NPC_DamageReceiver.cs の 25 行目付近にある Update() メソッド内に、以下のコードを追記します。
		// ホスト以外は処理をしない
		if( !MonobitEngine.MonobitNetwork.isHost )
		{
			return;
		}
  「ホスト権限を持たないクライアントでは、Update() を処理させない」というロジックを適用させます。


敵死亡時の同期制御

敵死亡処理を同期させるため、ホストから他クライアントにRPCメッセージを送信する

  ホスト側のクライアントで「敵をやっつけた」段階で、他クライアントに対しRPCメッセージを送信し、敵の死亡タイミングを同期させます。
  RPC については こちら を参照してください。

RPCメッセージを受信するために、MonobitEngine.MonoBehaviour を継承する

  現在開いている NPC_DamageReceiver.cs の 7 行目付近にあるクラス定義文にて、
  MonobitEngine.MonoBehaviour を継承するように変更を加えます。
public class NPC_DamageReceiver : MonobitEngine.MonoBehaviour {
  RPCメッセージを受信するためには、最低限 MonobitEngine.MonoBehaviour を継承している必要があります。

RPCを制御するクラス内に、MonobitView コンポーネント用のフィールドを用意する

  更に、RPC を送信するためには MonobitView のコンポーネント情報が必要ですので、これを用意しましょう。

  まず、該当するクラス内に、MonobitView コンポーネントを管理するための変数を用意します。
  NPC_DamageReceiver.cs の 10 行目付近に、以下のフィールドを追加してください。
    // MonobitView コンポーネント
    MonobitEngine.MonobitView m_MonobitView = null;
  前述までと同様、MonobitView コンポーネント本体の参照のための変数を用意します。

該当するクラスの Awake() で、MonobitView コンポーネントの情報を取得する

  更に、NPC_DamageReceiver.cs の 14 行目付近に、以下の Awake() メソッドのコードを追加します。
    void Awake()
    {
        // すべての親オブジェクトに対して MonobitView コンポーネントを検索する
        if (GetComponentInParent<MonobitEngine.MonobitView>() != null)
        {
            m_MonobitView = GetComponentInParent<MonobitEngine.MonobitView>();
        }
        // 親オブジェクトに存在しない場合、すべての子オブジェクトに対して MonobitView コンポーネントを検索する
        else if (GetComponentInChildren<MonobitEngine.MonobitView>() != null)
        {
            m_MonobitView = GetComponentInChildren<MonobitEngine.MonobitView>();
        }
        // 親子オブジェクトに存在しない場合、自身のオブジェクトに対して MonobitView コンポーネントを検索して設定する
        else
        {
            m_MonobitView = GetComponent<MonobitEngine.MonobitView>();
        }
    }

  ここも前述までにも説明した通り、親オブジェクトに存在する MonobitView を検索し、
  そのコンポーネント情報を m_MonobitView に格納します。

敵死亡のタイミングに合わせて RPC メッセージを発信する

  続けて、NPC_DamageReceiver.cs の 56 行目付近の Update() メソッド中に、敵キャラクタが死亡した処理を実行している箇所について
  以下のコードに置き換えます。
            // GetComponent<CharacterController>().enabled = false; // disable collision when dead.
            m_MonobitView.RPC("CharacterControllerOff", MonobitEngine.MonobitTargets.All, null);
  敵が死亡した後に「キャラクタコントローラを無効化する」処理が入っている箇所について、
  前章のチャットの際にも説明した「MonobitView.RPC」を利用します。

  このRPCメッセージの送信については、「ルーム内のプレイヤー全員」に対して実行するようにします。
  また、特に送信時に渡さなければならないパラメータはありませんので、ここでは null を代入しておきます。

敵死亡の通知について、RPC メッセージを受信する

  この状態で、NPC_DamageReceiver.cs の 14 行目付近に戻り、RPC受信メソッドとして以下のコードを追加します。
    [MunRPC]
    void CharacterControllerOff()
    {
        GetComponent<CharacterController>().enabled = false; // disable collision when dead.
    }
  これも前章のチャットの際にも説明しましたが、MonobitView.RPC() メソッドを使ったRPCメッセージの送信に対し、
  受信する関数を以下のように定義します。
・メソッド名の接頭に[MunRPC]のアトリビュートを付記すること。
・MonobitView.RPC() の第一引数と同じ名前のメソッド名で定義すること。
・MonobitView.RPC() の第三引数以降に対応するデータ型の引数値を定義すること。
 (今回は引数にnullを指定しているので、引数は無し)
  先ほど送信側の関数名に「CharaterControllerOff」と名付けましたので、ここでは同名のメソッド名で記述します。
  記述する内容は、先ほど MonobitView.RPC() によって書き換えられた処理を組み込みます。