私はどのようにロビーと複数ルームを実装したかといいますと、下のチュートリアル動画を09まで鬼のように見るところから始めました。
動画でやっている通りに書いていると何となくやり方が分かります。ところが、この動画09まで見ると3時間ぐらいあります。英語を聞きながらひたすら打ち続けることになりますが、そこは眠気に耐えて頑張りましょう。
それから07と08が表題とあんまり関係ないので、そこは飛ばしても大丈夫です。
一応私がこのチュートリアルを「ほぼ理解することなく」どのようにして流用に成功したかということについてだけ、書き残しておきたいと思います。
まず私がハマった所として、Photon Server Setting のAuto-Join Lobbyはオフにしておきましょう。
private void OnConnectedToMaster()が呼ばれるのはAuto-Join Lobby がオフの時だけです。
これをオンにしてしまうとprivate void OnConnectedToMaster()の中に書いてあるプレイヤー名の処理などが正しく行われないので、プレイヤー名が出ないバグが出ます。
部屋を作成する方法として2通りの方法が紹介されています。
これはゲームの用途にもよりますが、Syncではなく、Delayedを使用するのが良いかと思われます。
Delayed では、ゲーム開始時に部屋を見えなくするという機能がついています。
進行中のゲームが表示されると、プレイヤーが一人退出した際に、ゲーム途中の部屋が表示され、入室されるとおかしなことになるのであまりお勧めできません。
スクリプトでいうと以下の所です。
using UnityEngine; public class CurrentRoomCanvas : MonoBehaviour { public void OnClickStartSync()//Syncボタンを押すとこれが呼ばれる。 { if (!PhotonNetwork.isMasterClient)//マスタークライアントでなければ下記の処理は行えない。 return; PhotonNetwork.LoadLevel(2);//後述の理由より、ゲームシーンは2にしています。 } public void OnClickStartDelayed()//delayedボタンを押すとこれが呼ばれる。 { if (!PhotonNetwork.isMasterClient)//マスタークライアントでなければ下記の処理は行えない。 return; PhotonNetwork.room.IsOpen = false;//部屋に入室不可とする。 PhotonNetwork.room.IsVisible = false;//部屋を見えなくする。 PhotonNetwork.LoadLevel(2);//ゲームシーンに移動する。 } }
私はdelayedボタンを「はじめる」に改変して使っています。
それから、PlayerLayoutGroup.csのRoom Stateというのは使っていません。
結局部屋を隠したり見せたりできる機能なので、いらないということになります。
よって、その部分はコメントアウトしました。もちろんボタンも削除してあります。
public void OnClickRoomState() { // if (!PhotonNetwork.isMasterClient) // return; // PhotonNetwork.room.IsOpen = !PhotonNetwork.room.IsOpen; // PhotonNetwork.room.IsVisible = PhotonNetwork.room.IsOpen; }
このチュートリアルは09番の最後にエラーを吐いて、あとは自分で直してね。
簡単にできるから自分でやってね。と言って終わります。
なので、そのエラーは自分でつぶさないといけないのですが、私は結構時間がかかったので、その部分について共有したいと思います。
そのエラーというのはDDOL(DontDestroyOnLoad)がついているスクリプトは、シーンが切り替わっても文字通りDestroyされないのです。
チュートリアル通りにやると、DDOLがロビーのシーンに紐づいているので、ロビーに戻った時にDDOLを再び読んでしまい、スクリプトが複製されてしまいます。
これは常に一つだけあるのが正なので、再び呼ばないように工夫をする必要があり、チュートリアルでは、DDOLだけ入ったシーンを用意して、それを一度だけ読んであげるシステムにすればいいよとさらっと言っています。
で、実際私がそれをどのように実装したかといいますと、まず、DDOLというシーンを作成しました。シーンにはそれしか入っていません。
このシーンをデフォルトのシーンにしてあげて、2度と呼ばないということをしています。
ビルドセッティングで番号を確認すると下記のようになります。これはマウスでドラッグアンドドロップで入れ替えられるので簡単に好きな順番にできます。
この場合まず呼ばれるのはDDOLのシーンが初期状態です。
そこで、PlayerNetwork.csのprivate void Awake()にLoadLevel(1)(ロビー)を読むように書いてあげます。
すると瞬時にロビーが呼ばれるので、正しい順序でロビーとゲーム部屋とが呼ばれる準備ができます。
private void Awake() { Instance = this; PhotonView = GetComponent<PhotonView>(); PlayerName = "Player#" + Random.Range(1000, 9999); SceneManager.sceneLoaded += OnSceneFinishedLoading; PhotonNetwork.LoadLevel(1);//追記 }
もともとはロビーがLoadLevel(0)でゲームがLoadLevel(1)なので、それぞれすべてのコードについて、1、2とリナンバーする必要があります。これでDDOL(0)は一度しか呼ばれないのでエラーを回避することができました。
もう一つ私が悩んだのは、ゲームのエントリーポイントをどこにするかというものでした。
要するにこのチュートリアルのプログラムとゲームのプログラムをどのように繋ぐかということです。
それは私はPlayerNetwork.csのRPCの中に、メインのゲームの関数を呼んであげるという方法を取りました。
private void MasterLoadedGame() { PhotonView.RPC("RPC_LoadedGameScene", PhotonTargets.MasterClient); PhotonView.RPC("RPC_LoadGameOthers", PhotonTargets.Others); } private void NonMasterLoadedGame() { PhotonView.RPC("RPC_LoadedGameScene", PhotonTargets.MasterClient); } [PunRPC] private void RPC_LoadGameOthers() { PhotonNetwork.LoadLevel(2); } [PunRPC] private void RPC_LoadedGameScene() { PlayersInGame++; if(PlayersInGame == PhotonNetwork.playerList.Length) { print("All players are in the game scene."); PhotonView.RPC("RPC_CreatePlayer", PhotonTargets.MasterClient); PhotonView.RPC("RPC_CreatePlayer", PhotonTargets.Others); } } [PunRPC] private void RPC_CreatePlayer() { networkManager = GameObject.Find("NetworkManager").GetComponent<NetworkManager>(); networkManager.StartingProcess();//これがメインゲームの関数 }
最後に、私がハマったもう一つのバグについて書きたいと思います。
一度入室してゲームが始まった後、一度ロビーに戻ってもう一度ゲームを始めようと部屋に入ると、ゲームが始まらないというものでした。
これはPlayerNetwork.csにゲームに入った人数をカウントして、それがPlayer.lengthと等しい時のみにしかRPCを呼ばない仕様になっているからでした。
2回目に部屋に入室した際、カウントアップがリセットされずに2名からカウントアップが始まり4名になってしまうため、エラーがでていました。
[PunRPC] private void RPC_LoadedGameScene() { PlayersInGame++; if(PlayersInGame == PhotonNetwork.playerList.Length) { print("All players are in the game scene."); PhotonView.RPC("RPC_CreatePlayer", PhotonTargets.MasterClient); PhotonView.RPC("RPC_CreatePlayer", PhotonTargets.Others); } }
よって、PlayersInGameをリセットしてあげる必要があるということです。
私はPlayerNetwork.csの下記の部分にPlayersInGameのリセットを行うようにスクリプトを改変して対応しました。
private void OnSceneFinishedLoading(Scene scene, LoadSceneMode mode) { PlayersInGame = 0;// プレイヤー数のリセット if (scene.name == "Game") { if (PhotonNetwork.isMasterClient) MasterLoadedGame(); else NonMasterLoadedGame(); } }
以上で、私がハマったところは大体解説できたかなと思います。
複数ルームの実装はかなり大変でした。次なる目標はターン制ゲームの実装です。現時点で何をしていいか全くわかりません。どうなることやら。
もしよろしければ、Photonについて私が知りえる限りの情報を徹底的にまとめてみたので、下記の記事も読んで下さいね!