Photon Fusionを用いたオンラインゲームの制作で気になったこと
今回はPhoton Fusionを使用してオンラインマルチプレイゲームを制作中に気になったことや躓いた箇所と、解決に至るまでをまとめようと思います。
前提
今回作るゲームは最大四人でマッチングするオンラインマルチプレイゲームです。Playerの中からランダムに一人が鬼として選ばれ、鬼になったPlayerは他のPlayerを攻撃して気絶させることで鬼をそのPlayerに渡すことができます。一定時間後に鬼だったPlayerは敗北となり、残りのPlayerは他Playerを気絶させたときやステージ内のギミックを使用することによって得られるポイントによって順位が決まります。
①インゲームシステムの設計
まず最初に躓いた所は鬼の切り替えとキル通知のシステムを誰がどこで行うか、というところです。最初のアプローチとしては各Playerが鬼かどうかを判定するIsOgreというbool型の変数を用意して、それをPlayer内で切り替え、PlayerDataBaseという全員のPlayerのステータスを監視するクラス内のIsOgre及びHpをさらに別で監視するという手法をとっていましたが、文章を読んでもらってもわかる通り非常に処理が追いづらい設計となっていました。なので設計を見直すこととなったのですが、その際にPhoton Engine公式から出されているサンプルが参考になったので掲載します。
サンプル1
サンプル2
これらのサンプルを参考にして、PlayerDataBaseとPlayerの中間に一つクラスをかませてそのクラス内で鬼の切り替えやキル通知、PlayerDataBasdeの更新を行うことにしました。実際に作成したクラス図はこちらになります
あらかじめPlayerの気絶時に呼ばれるActionにInGameManagerのOnPlayerKilledを登録しておき、OnPlayerKilled内に鬼の切り替えとキル通知に関する処理を記述します。本来であればInGameManagerはあくまで中間層として存在し、具体的な処理は別のクラスで記述するほうが自然ですが、今回に限ってはオンラインゲームということや処理がそこまで複雑ではないためそのままInGameManager内に処理を記述しています。またGameLuncherとInGameManagerはそもそもNetWorkBehaviourを継承するか否かという違いがあり、その分内容もかなり変わるので分けて存在しています。
②INetworkRunnerCallbacksのコールバックについて
NetWorkRunnerからPlayer参加等のコールバックを受け取るためにINetworkRunnerCallbacksというインターフェースを継承し、クラスをコールバック対象にする必要があります。詳しくは以下の記事が参考になるので掲載します。
さて、このコールバック内のOnPlayerJoinedメソッドを使ってPlayerが入室した際に対応するHpバーを生成しようとしたのですがいろいろと問題が起こったので紹介します。ちなみにPlayerのSpawnでHpバーの生成処理を行うこともできましたが、その時点ではPlayerの最大人数が不確定であったり、後々のデバッグのしやすさを考慮したためMVPパターンに則した設計にしました。(PlayerとHPバーの結合を避けました)OnPlayerJoinedが呼ばれるタイミング
まずOnPlayerJoinedというメソッドは新しいプレイヤーが現在のセッションに参加したときに呼ばれます。そのためOnPlayerJoinedが呼ばれるタイミングではPlayerのインスタンス化が終わっていない場合があるのです。この問題に関しては当時モック版の作成中だったのでUniTaskでDelayを入れることで無理やり解決しましたが、実際に開発をする際にはフローをしっかりと決める必要があると感じました。
OnPlayerJoinedが呼ばれる回数
OnPlayerJoinedが呼ばれる回数に関してですが、
- ホストまたはクライアントに新しいプレイヤーが接続したとき
- 自分自身が接続したとき
の二種類の呼ばれるタイミングがあります。なのでクライアント側で同じタイミングで何回もOnPlayerJoinedが呼ばれてしまう問題がありました。これに関しては以下のようにNetWorkRunnerのGetPlayerObjectを使い、LocalPlayerかどうか判定してあげることで各Playerが入室する際に一度だけ呼ばれるようにしました。
public async void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
await UniTask.Delay(_waitFrame);
var obj = runner.GetPlayerObject(player);
if (_networkRunner.LocalPlayer == player)
{
UI_OnPlayerSpawned(obj.GetComponent<PlayerController>(),player);
}
}
まとめ
今回はPhoton Fusionを使用してオンラインマルチプレイゲームを制作中に気になったことや躓いた箇所と、解決に至るまでをまとめました。まだまだゲームは製作途中なのでまた気になったことがあれば随時更新していきたいと思います。最後までお読みいただき、ありがとうございました。
Discussion