ゲーム開発
Unity
UnrealEngine
C++
Blender
Houdini
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • Houdini
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20200508_01

      【Unity + PUN2】オンライン3Dアクションゲームの作り方をまとめる! (後編)

      UnityオンラインゲームPhoton
      2020-05-09

      \カタカタカタカタ/

      ↑一人でマルチプレイをするマイケルの図
      マイケル
      マイケル
      いや〜楽しいな〜〜〜
      エレキベア
      エレキベア
      何してるクマ??
      マイケル
      マイケル
      よくぞ聞いてくれたねエレキベア
      マイケル
      マイケル
      実は前作っていたゲームにPUN2を導入して
      マルチプレイができるようにしたのさ!
      エレキベア
      エレキベア
      そういえばPUN2の導入についても勉強したクマね


      ※前回作ったゲームの記事について
      【Unity】オンライン3Dアクションゲームの作り方をまとめる! (前編)


      ※PUN2導入と基本的な使い方について
      【PUN2】ネットワーク管理にPhotonを使ってみる 〜導入からルーム・ロビー管理まで〜

      マイケル
      マイケル
      そう、あれから
      エレキベアを倒すゲームに改造したり
      PUN2を導入してネットワーク対応したり
      いろいろ進んだんだよ!

      エレキベアリンチ
      ↑プレイヤーをリンチするエレキベア

      エレキベア
      エレキベア
      我ながら恐ろしい絵クマ
      マイケル
      マイケル
      おいてけぼりのドラゴンがかわいそうだぜ
      マイケル
      マイケル
      そんな感じで、進めてた参考書の路線は外してしまったわけだけど、
      ネットワーク対戦までできるようになるという目標はクリアしたので
      これからはオリジナルゲーム作成をしていくぜ!
      エレキベア
      エレキベア
      ついにオリジナルに踏み出すクマね
      マイケル
      マイケル
      とりあえず今回は一番苦労したPUN2を使ったマルチプレイ化についてメモとして残しておくよ!
      エレキベア
      エレキベア
      燃えてきたクマ〜〜

      PUN2を使ったマルチプレイゲーム化

      マイケル
      マイケル
      すでに一人用で作ったゲームにPUN2を導入する程で、
      大きく以下の手順について解説していきます!


       ・ ネットワーク上のオブジェクト作成と破棄
       ・ RPCでの呼び出し
       ・ 同期処理の設定
       ・ コールバックについて

      エレキベア
      エレキベア
      やったるクマ〜〜〜〜

      ネットワーク上のオブジェクト作成と破棄

      マイケル
      マイケル
      まずはネットワーク上にオブジェクトを生成しないといけないですね・・・
      マイケル
      マイケル
      生成方法については、オブジェクトにPhotonViewスクリプトをアタッチして、
      通常使用する「Instantiate()」メソッドをPhotonNetworkクラスから呼び出すようにすればOKです!
              // プレイヤーを登場させる
              if (player == null && networkManager.isClosed)
              {
                  Vector3 shiftVector = new Vector3(PhotonNetwork.CountOfPlayers * 1.5f, 0, 0);
                  player = PhotonNetwork.Instantiate("player", startPoint.position + shiftVector, startPoint.rotation, 0) as GameObject;
      ・・・略・・・
              }

      スクリーンショット 2020 05 07 23 43 23
      ↑PhotonViewスクリプトのアタッチ

      マイケル
      マイケル
      ちなみにオブジェクトの破棄も同様に、「Destroy()」メソッド をPhotonNetworkから呼び出すようにすればOKです!
      エレキベア
      エレキベア
      これはクマでもわかるクマ

      RPCでの呼び出し

      マイケル
      マイケル
      次にメソッドの呼び出し方についてだ!
      ネットワーク上にオブジェクトが複数あるというのは下の図のような状態になる。

      スクリーンショット 2020 05 08 0 03 40

      マイケル
      マイケル
      これまで一人で完結していたのが増えるわけだから、
      全員の処理を呼び出したり、特定のユーザの処理を呼び出したりなど、
      処理の実行範囲を指定するようにしないといけないんだ!
      エレキベア
      エレキベア
      複数プレイヤーいることを考慮して実行しないといけないクマね
      マイケル
      マイケル
      その通り!
      そこで使うのがRPCという機能で、これを使ってメソッド呼び出しを指定することでメソッドの呼び出し範囲を指定することができるんだ!
      マイケル
      マイケル
      ここではよく使う3パターンについて紹介するよ!

      1. 自分含めた全ユーザの処理を実行する場合

      マイケル
      マイケル
      まずはゲームクリアの通知など、全体に対して送りたい時の呼び方について!
          public void GameEnd()
          {
      ・・・略・・・
                  // 全てのユーザに結果を送信する
                  photonView.RPC("GameEndOnNetwork", RpcTarget.AllBuffered, winPlayerName);
              }
          }
      
          [PunRPC]
          void GameEndOnNetwork(string winPlayerName)
          {
      ・・・略・・・
          }
      マイケル
      マイケル
      上記のように引数に呼び出したいメソッド名と「RpcTarget.AllBuffered」を指定してRPCメソッドを呼び出すと全体に送ることができます!
      マイケル
      マイケル
      RPCで呼び出すメソッドには「[PunRPC]」を付けないといけないので注意してください。
      エレキベア
      エレキベア
      これがRPCでの呼び出し方なのクマね

      2. オーナーの処理のみを実行する場合

      マイケル
      マイケル
      次にオーナーのメソッドのみを呼び出したい場合の呼び方です!
      マイケル
      マイケル
      敵のオブジェクトなどは全体で一つのオブジェクトになるため、代表でオーナーが作成するというのが一般的です。
      そのような、共通のオブジェクトに対して処理を行う場合に使います!
          void Damage(AttackArea.AttackInfo attackInfo)
          {
      ・・・略・・・
      
              // 管理オブジェクトのDamageMineを呼び出す
              if (photonView.IsMine)
              {
                  DamageMine(attackInfo.attackPower);
              } else
              {
                  photonView.RPC("DamageMine", photonView.Owner, attackInfo.attackPower);
              }
          }
      
          [PunRPC]
          void DamageMine(int damage)
          {
              status.HP -= damage;
              if (status.HP <= 0)
              {
                  status.HP = 0;
                  // 体力0なので死亡ステートへ
                  ChangeState(State.Died);
              }
          }
      マイケル
      マイケル
      上記のようにphotonView.Ownerを指定して呼び出します。
      ちなみに「photonView.IsMine」では自身の作成したオブジェクトかを判定しています!
      エレキベア
      エレキベア
      自身のオブジェクトはRPCを使わずにメソッドを呼び出すクマね

      3. 特定のプレイヤーの処理のみ実行する場合

      マイケル
      マイケル
      そして最後に特定のユーザにの処理のみ呼び出す場合です。
          // 他のプレイヤーが接続してきたらサーバのコンピュータで呼び出される
          public override void OnPlayerEnteredRoom(Player player)
          {
              photonView.RPC("SetRemainTime", player, timeRemaining);
          }
          
          [PunRPC]
          void SetRemainTime(float time)
          {
              timeRemaining = time;
          }
      マイケル
      マイケル
      こちらは引数としてPlayerを渡すことで指定します!
      また、上記の処理内容はプレイヤーがルームに入室した差異に呼ばれるコールバック処理になります。
      マイケル
      マイケル
      以上、よく使う3つのパターンについて記述しましたが、
      RPCに指定するターゲットは上記以外にも複数あるので気になる方はリファレンスを参照してください。

      [参考]
      Photon Unity Networking 2 Public API

      エレキベア
      エレキベア
      たくさんあってわからんクマ

      同期処理の設定

      マイケル
      マイケル
      次に同期の設定をしていきます。
      オブジェクトを作成しただけでは自分以外のプレイヤーの動き等が反映されないため、
      PhotonViewの「Observed Components」に同期する情報を設定することで同期します!
      マイケル
      マイケル
      位置を同期させたいだけなら上記の様にTransformを指定してあげるだけでいいのですが、自身のスクリプト等を同期させたい場合には同期するスクリプトを作成してあげなければいけません。
      マイケル
      マイケル
      下記は一例で「CharacterStatus.cs」の変数を同期させるスクリプトです。
      作成したスクリプトを「Observed Components」に指定してあげることで同期が可能になりなります!
      // IPunObservableインターフェイスを実装
      public class CharaSynchronizer : MonoBehaviour, IPunObservable
      {
          // キャラクターステータス
          CharacterStatus status;
      
          // 同期処理
          public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
          {
              if (stream.IsWriting)
              {
                  // 送信
                  // 位置と向き
                  Vector3 pos = transform.position;
                  Quaternion rot = transform.rotation;
                  stream.Serialize(ref pos);
                  stream.Serialize(ref rot);
                  if (status != null)
                  {
                  	// CharacterStatus: HP
                      int hp = status.HP;
                      stream.Serialize(ref hp);
                  }
              }
              else
              {
                  // 受信
                  // 位置と向き
                  stream.Serialize(ref position);
                  stream.Serialize(ref rotation);
                  if (status != null)
                  {
                  	// CharacterStatus: HP
                      int hp = 0;
                      stream.Serialize(ref hp);
                      status.HP = hp;
                  }
              } 
          }
      }

      スクリーンショット 2020 05 09 1 30 18
      ↑作成したスクリプトを設定

      マイケル
      マイケル
      これでスクリプトの内容も同期する様になるはずです!
      エレキベア
      エレキベア
      いい感じに同期するクマ〜〜〜〜〜

      コールバックについて

      マイケル
      マイケル
      最後はコールバック処理について!
      特定の処理をしたあとのコールバック処理を行うには、それぞれにあったクラスを継承しなければいけません。
      エレキベア
      エレキベア
      導入のときにもいろいろコールバック処理があったクマ
      マイケル
      マイケル
      それと同じ様なイメージだよ!
      ここでは例として二つ紹介します!


      ・クライアントのルーム入室時のコールバック処理
      継承クラス: MonoBehaviourPunCallbacks
      コールバックメソッド: OnPlayerEnteredRoom()

      public class GameRuleCtrl : MonoBehaviourPunCallbacks
      {
      ・・・略・・・
          // 他のプレイヤーが接続してきたらサーバのコンピュータで呼び出される
          public override void OnPlayerEnteredRoom(Player player)
          {
              photonView.RPC("SetRemainTime", player, timeRemaining);
          }
      }


      ・ネットワークオブジェクト作成時のコールバック処理
      継承クラス: IPunInstantiateMagicCallback
      コールバックメソッド: OnPhotonInstantiate()

      public class DropItem : MonoBehaviour, IPunInstantiateMagicCallback
      {
      ・・・略・・・
          // ネットワーク越しにインスタンス生成された時に呼び出される
          void IPunInstantiateMagicCallback.OnPhotonInstantiate(PhotonMessageInfo info)
          {
              if (!info.photonView.IsMine)
              {
                  Destroy(GetComponent<Rigidbody>());
              }
          }
      }
      マイケル
      マイケル
      継承クラスを間違えただけでほんとに何も起こらなくなるので気をつけてください!
      マイケル
      マイケル
      また、その他のコールバックの処理については下記リファレンスを参照してください。

      [参考]
      Photon Unity Networking Photon.PunBehaviour Class Reference

      エレキベア
      エレキベア
      これもいろいろ種類がありすぎてわからんクマ

      おわりに

      マイケル
      マイケル
      以上、マルチプレイ実装のための簡単な機能説明でしたがいかがだったでしょうか。
      エレキベア
      エレキベア
      少しはネットワーク処理に詳しくなった気がするクマ
      マイケル
      マイケル
      俺もネットワークは全然詳しくないけど、
      使ってたら慣れてくるし、マルチプレイで遊べる様になるのはすごく楽しいので
      みなさんもまずは是非使ってみてください!
      エレキベア
      エレキベア
      拙者も自作ゲームでマルチプレイしてみたいクマね
      マイケル
      マイケル
      それじゃ俺の作ったゲームで対戦しようぜ!
      エレキベア
      エレキベア
      自分を倒すのはコリゴリクマ・・・

      【Unity + PUN2】オンライン3Dアクションゲームの作り方をまとめる! (後編) 〜完〜


      UnityオンラインゲームPhoton
      2020-05-09

      関連記事
      【Unity】Timeline × Excelでスライドショーを効率よく制作する
      2024-10-31
      【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
      2024-05-28
      【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
      2024-04-14
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-28
      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
      2024-03-28
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27