"それ"、とは…
正直何を示しているのか掴みづらいところなんですが、なんというか、今指定されているプレイヤー、みたいな感じでしょうか。
チュートリアルは以下のページを参考にしています。
目次
プレイヤーIDを把握する
順番に見ていきましょう。
自分のIDはPhotonNetwork.player.IDで取得できます。
チュートリアルのコードでは、OnJoinRoomで入ったときに自分一人であれば、まず自分を"それ"として認識しているようです。
public static int playerWhoIsIt; void OnJoinedRoom() { // game logic: if this is the only player, we're "it" if (PhotonNetwork.playerList.Length == 1) { playerWhoIsIt = PhotonNetwork.player.ID; } Debug.Log("playerWhoIsIt: " + playerWhoIsIt); }
RandomMatchmakerにplayerWhoIsItと、OnJoinedRoomの部分にplayerWhoIsItへの代入を追記します。
実行するとコンソールに
playerWhoIsIt: 1
と出ますね。
"それ"を知らせる
どのプレイヤーが現在指定されているかを知らせるためのRPCコールを作ります。
[PunRPC] void TaggedPlayer(int playerID) { playerWhoIsIt = playerID; Debug.Log("TaggedPlayer: " + playerID); }
というコールを用意します。で、こちらを追加していく前に、そもそも管理用のPhotonViewを作っておくようにします。
シーンに管理用のPhotonViewを用意する
今までは、自キャラのモンスターにルーム処理などもさせてしまっていましたが、これでは作成を進めていくと不便になっていきます。
なので、管理用のPhotonViewをひとつ、シーンに作るようにします。
シーンに「Script」というGameObjectを追加して、それにPhotonViewコンポーネントを追加。
さらに、次のクラスを作成して、「GameLogic」としてScriptオブジェクトに追加します。
using UnityEngine; using System.Collections; public class GameLogic : Photon.PunBehaviour { private static PhotonView ScenePhotonView; public static int playerWhoIsIt; void Start() { ScenePhotonView = this.GetComponent<PhotonView>(); } public override void OnJoinedRoom() { // game logic: if this is the only player, we're "it" if (PhotonNetwork.playerList.Length == 1) { playerWhoIsIt = PhotonNetwork.player.ID; } Debug.Log("playerWhoIsIt: " + playerWhoIsIt); } public override void OnPhotonPlayerConnected(PhotonPlayer player) { Debug.Log("OnPhotonPlayerConnected: " + player); // when new players join, we send "who's it" to let them know // only one player will do this: the "master" if (PhotonNetwork.isMasterClient) { TagPlayer(playerWhoIsIt); } } public static void TagPlayer(int playerID) { Debug.Log("TagPlayer: " + playerID); ScenePhotonView.RPC("TaggedPlayer", PhotonTargets.All, playerID); } [PunRPC] void TaggedPlayer(int playerID) { playerWhoIsIt = playerID; Debug.Log("TaggedPlayer: " + playerID); } }
先ほどのPlayerIDについての処理も、ここに含めてしまう感じで。
OnPhotonPlayerConnectedで誰かが入室したときに、自身がマスタークライアント(いろいろな処理をとりまとめている、クラスタに単一のクライアント)であれば"それ"が指しているプレイヤーIDを全体に送信しています。
今、どれが指定されているかをマスタークライアントで処理している感じですね。
この状態で2つ以上のクライアントでログインしていくと、最初のプレイヤーIDが全体に送られます。
…といってもConsoleへの出力なので、Editorのクライアントを後から実行するようにしてみるなどしないとわかりづらいかもしれません。
退出時に"それ"だったら別のプレイヤーにする
ここでは、指定されているプレイヤーが退出してしまった場合に、そのままだと存在しないプレイヤーを指定している状態になってしまいます。
なので、退出時の処理も作ってみます。
public override void OnPhotonPlayerDisconnected(PhotonPlayer player) { Debug.Log("OnPhotonPlayerDisconnected: " + player); if (PhotonNetwork.isMasterClient) { if (player.ID == playerWhoIsIt) { // if the player who left was "it", the "master" is the new "it" TagPlayer(PhotonNetwork.player.ID); } } }
こちらをPhotonViewManagerに追加。
退出したプレイヤーのIDが指定されていたプレイヤーだった場合、マスタークライアントのプレイヤーIDで指定しなおすようにしています。
クリックしたら切り替えるようにする
最後に、モンスターをクリックしたら、そいつに指定を切り替えるようにしてみます。
これはまた新しいスクリプト「ClickDetector」を作成して、monsterprefabに追加します。
using UnityEngine; using System; public class ClickDetector : MonoBehaviour { void Update() { // if this player is not "it", the player can't tag anyone, so don't do anything on collision if (PhotonNetwork.player.ID != GameLogic.playerWhoIsIt) { return; } if (Input.GetButton("Fire1")) { GameObject goPointedAt = RaycastObject(Input.mousePosition); if (goPointedAt != null && goPointedAt != this.gameObject && !goPointedAt.name.Equals("Plane", StringComparison.OrdinalIgnoreCase)) { PhotonView rootView = goPointedAt.transform.root.GetComponent<PhotonView>(); GameLogic.TagPlayer(rootView.owner.ID); } } } private GameObject RaycastObject(Vector2 screenPos) { RaycastHit info; if (Physics.Raycast(Camera.main.ScreenPointToRay(screenPos), out info, 200)) { return info.collider.gameObject; } return null; } }
これで実行してみると、"それ"として指定されているほうのクライアントであれば、クリックしたモンスターのクライアントに"それ"の指定を渡すことができるようになっています。
"それ"になっていないクライアントではクリックしても何も起こりません。
MarcoとPoloのボタンを、"それ"かどうかで分ける
あと、ボタンの表示について分けてみます。
要は、指定されたクライアントであればMarcoを、そうでなければPoloのボタンを出すようにします。
RandomMatchMaker#OnGUIで、ボタンを表示しているところを次のように変更します。
void OnGUI() { GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString()); if (PhotonNetwork.connectionStateDetailed == ClientState.Joined) { bool shoutMarco = GameLogic.playerWhoIsIt == PhotonNetwork.player.ID; if (shoutMarco && GUILayout.Button("Marco!")) { this.myPhotonView.RPC("Marco", PhotonTargets.All); } if (!shoutMarco && GUILayout.Button("Polo!")) { this.myPhotonView.RPC("Polo", PhotonTargets.All); } } }
これで実行すると、自分が"それ"であればMarcoボタンが、そうでなければPoloボタンが表示され、クリックで切り替わるようになります。
というわけで、Photonのチュートリアルをページに沿ってざっと流してみました。
チュートリアルに書いてあることはここまでですね。お疲れさまでした!
元のチュートリアルはところどころコードが古いようだったり、日本語がおかしかったり(直訳?)するところがあったので、補足みたいな扱いで書いてみました。
PhotonViewあたりの扱い方や、RPCコールあたりがざっと触れられているので、Photonを使ってマルチクライアントのアプリを作成しようとするときには、一度チュートリアルを通してみるとよいと思います。
その際に参考になれば幸いです。