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

スポンサーリンク
PC創作
\カタカタカタカタ/
IMG 6036↑一人でマルチプレイをするマイケルの図
マイケル
マイケル
いや〜楽しいな〜〜〜
IMG 6038
エレキベア
エレキベア
何してるクマ??
マイケル
マイケル
よくぞ聞いてくれたねエレキベア
マイケル
マイケル
実は前作っていたゲームに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. 自分含めた全ユーザの処理を実行する場合

スクリーンショット 2020 05 08 0 03 58
マイケル
マイケル
まずはゲームクリアの通知など、全体に対して送りたい時の呼び方について!
    public void GameEnd()
    {
・・・略・・・
            // 全てのユーザに結果を送信する
            photonView.RPC("GameEndOnNetwork", RpcTarget.AllBuffered, winPlayerName);
        }
    }

    [PunRPC]
    void GameEndOnNetwork(string winPlayerName)
    {
・・・略・・・
    }
マイケル
マイケル
上記のように引数に呼び出したいメソッド名と「RpcTarget.AllBuffered」を指定してRPCメソッドを呼び出すと全体に送ることができます!
マイケル
マイケル
RPCで呼び出すメソッドには「[PunRPC]」を付けないといけないので注意してください。
エレキベア
エレキベア
これがRPCでの呼び出し方なのクマね

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

スクリーンショット 2020 05 08 0 04 06
マイケル
マイケル
次にオーナーのメソッドのみを呼び出したい場合の呼び方です!
マイケル
マイケル
敵のオブジェクトなどは全体で一つのオブジェクトになるため、代表でオーナーが作成するというのが一般的です。
そのような、共通のオブジェクトに対して処理を行う場合に使います!
    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. 特定のプレイヤーの処理のみ実行する場合

スクリーンショット 2020 05 08 0 04 14
マイケル
マイケル
そして最後に特定のユーザにの処理のみ呼び出す場合です。
    // 他のプレイヤーが接続してきたらサーバのコンピュータで呼び出される
    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」に同期する情報を設定することで同期します!
スクリーンショット 2020 05 09 1 30 03
マイケル
マイケル
位置を同期させたいだけなら上記の様に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アクションゲームの作り方をまとめる! (後編) 〜完〜

コメント