仮想と物理とエトセトラ

xRや物理とかごった煮の備忘録的技術ブログ

Photon(PUN2)でルーム情報を表示する

今日は久しぶりにPhotonについての記事です。

これまでの実装では1つのルームに対して入室していましたが、複数のルームに分けて情報共有することも可能です。
これを実施することで、同じPhoton CloudまたはPhoton Serverで複数組の情報共有が可能になります。

今回は公式の下記記事を参考にしています。

doc.photonengine.com

準備

アプリ起動時ロビーに入室させる

まずはこれまで作成したPhotonManager.csを編集し、Photon Cloud/Serverに接続後、ロビーに入室するようにします。
ルームリストはロビー入室時にしか確認することができません。
今回、ロビーで取得するルームリストは公式のUpdateCachedRoomListを流用しています。
doc.photonengine.com

  • PhotonManager.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Photon.Pun;
using Photon.Realtime;
using Photon.Voice.PUN;

namespace MRTKPhotonTest
{
    /// <summary>
    /// Photon接続用クラス
    /// </summary>
    public class PhotonManager : MonoBehaviourPunCallbacks
    {

        /// <summary>
        /// 自インスタンス
        /// </summary>
        public static PhotonManager instance;

        /// <summary>
        /// ユーザ用プレハブ
        /// </summary>
        [SerializeField]
        private GameObject UserPrefab;

        /// <summary>
        /// 共有基準点
        /// </summary>
        [SerializeField]
        public Transform SharingBaseTransform;

        /// <summary>
        /// ルーム情報
        /// </summary>
        /// <typeparam name="string"></typeparam>
        /// <typeparam name="RoomInfo"></typeparam>
        /// <returns></returns>
        private Dictionary<string, RoomInfo> cachedRoomList = new Dictionary<string, RoomInfo>();   // 2022/03/21 追加

        void Awake()
        {
            // シングルトン化
            if (instance == null)
            {
                // 未作成の場合作成
                instance = this;
                DontDestroyOnLoad(this.gameObject);
            }
            else
            {
                // すでに作成されている場合削除
                Destroy(this.gameObject);
            }
        }
        void Start()
        {
            // PhotonCloudに接続
            Connect();
        }
        public void Connect()
        {
            if (!PhotonNetwork.IsConnected)
            {
                // 未接続の場合PhotonServerSettings.assetに従って接続
                PhotonNetwork.ConnectUsingSettings();
            }
        }

        private void UpdateCachedRoomList(List<RoomInfo> roomList)   // 2022/03/21 追加
        {
            for (int i = 0; i < roomList.Count; i++)
            {
                RoomInfo info = roomList[i];
                if (info.RemovedFromList)
                {
                    cachedRoomList.Remove(info.Name);
                }
                else
                {
                    cachedRoomList[info.Name] = info;
                }
            }
        }


        /// <summary>
        /// サーバ接続した場合のCallBack
        /// </summary>
        public override void OnConnectedToMaster()
        {
            PhotonNetwork.JoinLobby();   // 2022/03/21 編集
        }

        public void JoinRoom(string roomName)   // 2022/03/21 追加
        {
            // ルーム名roomNameに参加する。なければ作る。
            PhotonNetwork.JoinOrCreateRoom(roomName, new RoomOptions(), TypedLobby.Default);
        }

        public void LeaveRoom()   // 2022/03/21 追加
        {
            if(PhotonNetwork.InRoom)
            {
                PhotonNetwork.LeaveRoom();
            }
        }

        /// <summary>
        /// ルーム入室した際のCallBack
        /// </summary>
        public override void OnJoinedRoom()
        {
            Debug.Log("Entered Photon Room!");
            if (UserPrefab != null)
            {
                // ユーザオブジェクトを生成する
                GameObject userObject = PhotonNetwork.Instantiate(this.UserPrefab.name, new Vector3(0f, 0.5f, 0.8f), Quaternion.identity, 0);

                if (SharingBaseTransform != null)
                {
                    // 共有基準点の子オブジェクトにする
                    userObject.transform.parent = SharingBaseTransform;
                }
            }
        }

        /// <summary>
        /// ルームリスト更新時処理
        /// </summary>
        /// <param name="roomList"></param>
        public override void OnRoomListUpdate(List<RoomInfo> roomList)   // 2022/03/21 追加
        {
            UpdateCachedRoomList(roomList);
        }

        /// <summary>
        /// ロビー退出時処理
        /// </summary>
        public override void OnLeftLobby()   // 2022/03/21 追加
        {
            cachedRoomList.Clear();
        }

        /// <summary>
        /// 接続切断時処理
        /// </summary>
        /// <param name="cause"></param>
        public override void OnDisconnected(DisconnectCause cause)   // 2022/03/21 追加
        {
            cachedRoomList.Clear();
        }

        /// <summary>
        /// ルーム情報取得メソッド
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, RoomInfo> GetRoomInfo()   // 2022/03/21 追加
        {
            return cachedRoomList;
        }

    }
}

これで、アプリ起動時に自動でPhoton Cloud/Serverに接続し、ロビーに入室する処理が作成できました。

ルーム名を指定して入室する

今回は別スクリプトでルームリスト情報の表示や、ルーム入室を行う必要があります。
次にその処理を作成します。

まずは、入室したいルーム名を入力するためのInputFieldを用意します。
Hierarchy上で右クリックしてInput Filed - TextMeshProを選択します。
f:id:napo909:20220321151146p:plain

作成したオブジェクトのうち、Canvasを選択し、Convert to MRTK Canvasを選択します。
f:id:napo909:20220321151414p:plain

これでCanvasサイズをシーン中で自由に変更できるようになるので、使いやすい大きさに変更します。
次に、Input Fieldの隣に入室用のボタンを2つ、TextMeshProを1つ追加します。
Hierarchy上で右クリックし、TextMeshProを選択します。 f:id:napo909:20220321153657p:plain
ボタンは1つは入室用、もう一つは退室用です。
f:id:napo909:20220321160442p:plain f:id:napo909:20220321160725p:plain 下記スクリプトをアタッチし、入室、退室ボタンにそれぞれメソッドを設定します。

  • EnterRoom.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

using Photon.Pun;
using Photon.Realtime;
using Photon.Voice.PUN;

namespace MRTKPhotonTest
{
    public class EnterRoom : MonoBehaviour
    {
        [SerializeField]
        private TMP_InputField inputFiled;

        [SerializeField]
        private TextMeshPro logText;
        
        // Start is called before the first frame update
        void Start()
        {

        }

        // Update is called once per frame
        void Update()
        {

        }

        public void LeaveRoom()
        {
            PhotonManager.instance.LeaveRoom();
            logText.text = $"Left room";
        }

        public void EnterNamedRoom()
        {
            string roomName = inputFiled.text;
            if(!string.IsNullOrEmpty(roomName))
            {
                PhotonManager.instance.JoinRoom(roomName);
                logText.text = $"Entered {roomName} room";
            }
        }
    }
}

f:id:napo909:20220321160521p:plain

f:id:napo909:20220321153511p:plain

これで、ロビー入室後のルーム入退室の準備ができました。

ルーム情報を表示する

最後にルーム情報を表示する設定を行います。
今回はテキストベースで既存のルーム名と、入室人数を表示する簡単なものにしています。
Hierarchy上で右クリックし、TextMeshProを選択します。 f:id:napo909:20220321153657p:plain

任意の大きさ、位置に設定して下記スクリプトをアタッチします。
f:id:napo909:20220321153820p:plain

  • ShowRoomInfo.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Photon.Pun;
using Photon.Realtime;
using Photon.Voice.PUN;


namespace MRTKPhotonTest
{
    public class ShowRoomInfo : MonoBehaviour
    {
        /// <summary>
        /// 更新間隔(秒)
        /// </summary>
        [SerializeField]
        private float timeInterval = 5f;

        /// <summary>
        /// 更新カウンタ
        /// </summary>
        private float timeCount = 0f;

        /// <summary>
        /// ルーム情報表示テキスト
        /// </summary>
        [SerializeField]
        private TextMeshPro infoText;

        // Start is called before the first frame update
        void Start()
        {

        }

        // Update is called once per frame
        void Update()
        {
            timeCount += Time.deltaTime;
            if (timeCount > timeInterval)
            {
                ShowRoomInfomation();
                timeCount = 0;
            }

        }

        /// <summary>
        /// ルーム情報表示メソッド
        /// </summary>
        private void ShowRoomInfomation()
        {
            infoText.text = "";
            if (PhotonNetwork.InLobby)
            {
                Dictionary<string, RoomInfo> roomInfoDict = PhotonManager.instance.GetRoomInfo();

                foreach (var info in roomInfoDict)
                {
                    infoText.text += $"RoomName:{info.Key}, Num of User:{info.Value.PlayerCount}\n";
                }
            }

        }
    }
}

f:id:napo909:20220321154018p:plain

これで存在するルーム情報を表示する準備ができました。

動作確認

HoloLens2側で先にルーム「test」に入室した後に、PC側でアプリを起動すると、ルーム「test」の情報が表示されました。
また、ルーム「test」に入室すると、HoloLens2側のユーザが表示され、別のルーム「test2」の場合はルームを新規作成しているので、誰もいない新規ルームに入室できました。