Chapter 09

🤝 接続の機能とコールバック

o8que
o8que
2021.04.09に更新

ここまでは、プレイヤーが既にルームに参加していることを前提にして、同じルーム内の他プレイヤーとの同期(🔄)方法について解説してきました。ここからは、その前提になっていた接続(🤝)方法について解説していきます。

まず、このチャプターでは、接続やマッチメイキングで使用するPUN2の機能を詳しく紹介します。

https://doc.photonengine.com/ja-jp/pun/current/lobby-and-matchmaking/matchmaking-and-lobby

接続

マスターサーバーへ接続

Photonのサーバーに接続するには、PhotonNetwork.ConnectUsingSettings()を使用します。成功すると、マスターサーバーへ接続された状態になります。

// PhotonServerSettingsの設定内容を使って、マスターサーバーへ接続する
PhotonNetwork.ConnectUsingSettings();
結果 呼ばれるコールバック
成功 OnConnectedToMaster()
失敗 OnDisconnected()

Photonのサーバーから切断

Photonのサーバーから切断するには、PhotonNetwork.Disconnect()を使用します。マスターサーバーとゲームサーバーのどちらに接続している状態でも、正しく動作します。

// Photonのサーバーから切断する
PhotonNetwork.Disconnect();
結果 呼ばれるコールバック
完了 OnDisconnected()

接続関連のコールバック

MonoBehaviourPunCallbacksを継承しているスクリプトは、マスターサーバーへ接続した時や、Photonのサーバーから切断された時のコールバックを受け取ることができます。Photonのサーバーから切断された時には、コールバックの引数から切断された原因(DisconnectCause)を取得できます。

using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

public class ConnectionCallbacksSample : MonoBehaviourPunCallbacks
{
    // マスターサーバーへの接続が成功した時に呼ばれるコールバック
    public override void OnConnectedToMaster() {
        Debug.Log("マスターサーバーに接続しました");
    }

    // Photonのサーバーから切断された時に呼ばれるコールバック
    public override void OnDisconnected(DisconnectCause cause) {
        Debug.Log($"サーバーとの接続が切断されました: {cause.ToString()}");
    }
}

ルーム作成

ルームの作成

新規でルームを作成するには、PhotonNetwork.CreateRoom()を使用します。ルームの作成が成功すると、作成したプレイヤーは自動的にそのルームへ参加します。

// "Room"という名前のルームを作成する
PhotonNetwork.CreateRoom("Room");
結果 呼ばれるコールバック
成功 OnCreateRoom()、OnJoinedRoom()
失敗 OnCreateRoomFailed()

第一引数には作成するルーム名を渡しますが、既に同じルーム名のルームが存在する場合には、ルームの作成が失敗します。ルーム名にnullか""を渡すと、ユニークなルーム名が自動生成されるようになっているので、ルームの作成の失敗をなるべく回避したい時に活用できるでしょう。

// ユニークなルーム名を自動生成してルームを作成する
PhotonNetwork.CreateRoom(null);

ルーム作成のコールバック

MonoBehaviourPunCallbacksを継承しているスクリプトは、ルーム作成関連のコールバックを受け取ることができます。ルームの作成が失敗した時には、コールバックの引数からエラーメッセージを取得することができます。

using Photon.Pun;
using UnityEngine;

public class CreateRoomCallbacksSample : MonoBehaviourPunCallbacks
{
    // ルームの作成が成功した時に呼ばれるコールバック
    public override void OnCreatedRoom() {
        Debug.Log("ルームの作成に成功しました");
    }

    // ルームの作成が失敗した時に呼ばれるコールバック
    public override void OnCreateRoomFailed(short returnCode, string message) {
        Debug.Log($"ルームの作成に失敗しました: {message}");
    }
}

ルーム設定

PhotonNetwork.CreateRoom()の第二引数から、ルーム設定(RoomOptions)を渡すこともできます。

// ルームのカスタムプロパティの初期値
var initialProps = new Hashtable();
initialProps["DisplayName"] = $"{PhotonNetwork.NickName}の部屋";
initialProps["Message"] = "誰でも参加OK!";

// ロビーのルーム情報から取得できるカスタムプロパティ(キーの配列)
var propsForLobby = new[] { "DisplayName", "Message" };

// 作成するルームのルーム設定を行う
var roomOptions = new RoomOptions();
roomOptions.MaxPlayers = 4;
roomOptions.CustomRoomProperties = initialProps;
roomOptions.CustomRoomPropertiesForLobby = propsForLobby;

PhotonNetwork.CreateRoom("Room", roomOptions);

ルーム作成時によく使うルーム設定の値を、以下の表に示します。

名前 デフォルト値     説明
roomOptions.MaxPlayers 0(無制限) ルームに参加できる最大プレイヤー数
roomOptions.IsOpen true ルームへの参加が許可されているかどうか
roomOptions.IsVisible true ルームが公開されている(ロビーからルーム情報が取得できる)かどうか
roomOptions. CustomRoomProperties null ルームのカスタムプロパティの初期値
roomOptions. CustomRoomPropertiesForLobby new string[0] ロビーのルーム情報から取得できるカスタムプロパティ(キーの配列)

ルーム参加

ルームへ参加する機能は複数用意されているので、用途に応じて使い分けましょう。

ルーム名を指定してルームへ参加

最もお手軽にルームへ参加するなら、PhotonNetwork.JoinOrCreateRoom()が便利です。第一引数で指定したルーム名のルームが、既に存在しているなら参加、存在しなければ新規でルームを作成してから参加します。既にルームが存在していても、ルームが満員、または、ルームへの参加が許可されていない場合などは、ルームの参加に失敗します。

// "Room"という名前のルームに参加する(ルームが存在しなければ作成して参加する)
PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions(), TypedLobby.Default);
結果 呼ばれるコールバック
成功(既に存在するルームへ参加) OnJoinedRoom()
成功(新規でルームを作成して参加) OnCreateRoom()、OnJoinedRoom()
失敗 OnJoinRoomFailed()

ルームが既に存在していることがわかっているなら、PhotonNetwork.JoinRoom()を使用してルームへ参加できます。当然ですが、ルームが存在しなければ失敗します。PhotonNetwork.JoinOrCreateRoom()と同じように、ルームが満員、または、ルームへの参加が許可されていない場合などでも失敗します。

// "Room"という名前のルームに参加する(ルームが存在しなければ失敗)
PhotonNetwork.JoinRoom("Room");
結果 呼ばれるコールバック
成功 OnJoinedRoom()
失敗 OnJoinRoomFailed()

ランダムなルームへ参加

PhotonNetwork.JoinRandomRoom()で、既に存在しているルームの中の一つにランダムに参加できます。ルームが満員、ルームへの参加が許可されてない、ルームが非公開、これらの一つでも当てはまるルームは、ランダムの対象から除外されます。ランダムの対象に当てはまるルームが一つも存在しない場合は、ルームの参加に失敗します。

// 既に存在するランダムなルームに参加する
PhotonNetwork.JoinRandomRoom();
結果 呼ばれるコールバック
成功 OnJoinedRoom()
失敗 OnJoinRandomFailed()

PhotonNetwork.JoinRandomRoom()の第一引数と第二引数で、ルームのカスタムプロパティと、最大参加人数の絞り込み条件を指定することもできます。絞り込み条件を指定すると、条件と一致するカスタムプロパティの値と、最大参加人数が設定されているルームのみが、ランダムの対象になります。

// ルームのカスタムプロパティの絞り込み条件
var expectedProps = new Hashtable();
expectedProps["Mode"] = "TeamDeathmatch";

// 既に存在するランダムなルームに(条件を絞り込んで)参加する
PhotonNetwork.JoinRandomRoom(expectedProps, 8);

また、第一引数にnullか、第二引数に0を渡すと、片方の絞り込み条件のみを指定できます。

// ルームのカスタムプロパティの絞り込み条件のみ指定する
PhotonNetwork.JoinRandomRoom(expectedProps, 0);
// 最大参加人数の絞り込み条件のみ指定する
PhotonNetwork.JoinRandomRoom(null, 8);

ルーム参加のコールバック

MonoBehaviourPunCallbacksを継承しているスクリプトは、ルーム参加関連のコールバックを受け取ることができます。ルームへの参加が失敗した時に呼ばれるコールバックは、ルーム名を指定した時と、ランダムの時とで、別に用意されています。ランダムなルームへの参加が失敗するのは、ランダムで参加できるルームが存在しない場合がほとんどなので、コールバックから新規でルームを作成する処理などを入れておくと良いでしょう。

using Photon.Pun;
using UnityEngine;

public class JoinRoomCallbacksSample : MonoBehaviourPunCallbacks
{
    // ルームへの参加が成功した時に呼ばれるコールバック
    public override void OnJoinedRoom() {
        Debug.Log("ルームへ参加しました");
    }

    // ルーム名を指定したルームへの参加が失敗した時に呼ばれるコールバック
    public override void OnJoinRoomFailed(short returnCode, string message) {
        Debug.Log($"ルームへの参加に失敗しました: {message}");
    }

    // ランダムなルームへの参加が失敗した時に呼ばれるコールバック
    public override void OnJoinRandomFailed(short returnCode, string message) {
        // ランダムで参加できるルームが存在しないなら、新規でルームを作成する
        PhotonNetwork.CreateRoom(null);
    }
}

ルーム退出

ルームから退出

ルームから退出するには、PhotonNetwork.LeaveRoom()を使用します。ルームから退出した後は、元のマスターサーバーへ再び転送されるので、OnConnectedToMaster()コールバックも呼ばれます。

// ルームから退出する
PhotonNetwork.LeaveRoom();
結果 呼ばれるコールバック
完了 OnLeftRoom()、OnConnectedToMaster()

ルーム退出のコールバック

MonoBehaviourPunCallbacksを継承しているスクリプトは、ルーム退出のコールバックを受け取ることができます。

using Photon.Pun;
using UnityEngine;

public class LeftRoomCallbacksSample : MonoBehaviourPunCallbacks
{
    // ルームから退出した時に呼ばれるコールバック
    public override void OnLeftRoom() {
        Debug.Log("ルームから退出しました");
    }
}

ロビー

ロビーへ参加

PhotonNetwork.JoinLobby()で、ロビーへ参加できます。ロビーへ参加している間は、ルームリストが更新された時に呼ばれるコールバック(OnRoomListUpdate())を受け取ることができます。

// ロビーへ参加する
PhotonNetwork.JoinLobby();
結果 呼ばれるコールバック
完了 OnJoinedLobby()

ロビーから退出

ロビーから退出するには、PhotonNetwork.LeaveLobby()を使用します。ルームへ参加する時には、自動的にマスターサーバーを切断することになるため、明示的にロビーから退出する必要はありません。

// ロビーから退出する
PhotonNetwork.LeaveLobby();
結果 呼ばれるコールバック
完了 OnLeftLobby()

ロビー関連のコールバック

MonoBehaviourPunCallbacksを継承しているスクリプトは、ロビー関連のコールバックを受け取ることができます。ルームリストが更新された時に呼ばれるコールバックの引数には、更新されたルーム情報(RoomInfo)の差分のみが追加されています。

using System.Collections.Generic;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

public class LobbyCallbacksSample : MonoBehaviourPunCallbacks
{
    // ロビーへ参加した時に呼ばれるコールバック
    public override void OnJoinedLobby() {
        Debug.Log("ロビーへ参加しました");
    }

    // ルームリストが更新された時に呼ばれるコールバック
    public override void OnRoomListUpdate(List<RoomInfo> roomList) {
        foreach (var info in roomList) {
            if (!info.RemovedFromList) {
                Debug.Log($"ルーム更新: {info.Name}({info.PlayerCount}/{info.MaxPlayers})");
            } else {
                Debug.Log($"ルーム削除: {info.Name}");
            }
        }
    }

    // ロビーから退出した時に呼ばれるコールバック
    public override void OnLeftLobby() {
        Debug.Log("ロビーから退出しました");
    }
}

🌶 オフラインモード

PUN2ではオフラインモード(Offline Mode)を有効にすることで、PhotonNetworkやPhotonViewなどの機能を、サーバーに接続せずにローカルで動作させることができます。この機能を使えば、シングルプレイモードを実装する時でもオンラインかオフラインかで条件分岐するような処理がほぼ不要になり、同じコードをそのまま再利用できます。

using Photon.Pun;
using UnityEngine;

public class OfflineModeSample : MonoBehaviour
{
    public bool isOffline = false;

    public void Connect() {
        if (!isOffline) {
            PhotonNetwork.ConnectUsingSettings();
        } else {
            PhotonNetwork.OfflineMode = true;
        }
    }

    public void Disconnect() {
        if (!isOffline) {
            PhotonNetwork.Disconnect();
        } else {
            PhotonNetwork.OfflineMode = false;
        }
    }
}

これはあくまで、オンラインで問題なく動作するコードがオフラインでも動作するようになる機能です。オフラインモード上で動作するコードを実装したとしても、オンラインで動作することが保証されるわけではないことに注意してください。オンラインでちゃんと動くかどうかの確認・テストは、必ずオンラインで行うようにしましょう。