1 - ロビー
サーバーへの接続およびルームへのアクセスと作成
まずはこのチュートリアルの核心である、Photon Cloudサーバーへの接続とルーム の作成や参加に取り組みましょう。
新しいシーンを作成し、
Launcher.unity
として保存します。新しいCスクリプト
Launcher
を作成します。Hierarchyに
Launcher
という名前の空のGameObjectを作成します。C#スクリプト
Launcher
を GameObjectLauncher
にアタッチします。C#スクリプト
Launcher
を編集して、コンテンツを以下のようにします。コーディングのヒント ただコードをコピーペーストするのではなくて、1つ1つ自分の手でタイピングすることが大切です。そのほうが覚えられますから。 コメントの記述はとても簡単です。メソッドかプロパティの上に///
とタイプすると、自動的にスクリプトエディターが<summary>
タグなどの構造化されたコメントを生成します。C#
using UnityEngine; using Photon.Pun; namespace Com.MyCompany.MyGame { public class Launcher : MonoBehaviour { #region Private Serializable Fields #endregion #region Private Fields /// <summary> /// This client's version number. Users are separated from each other by gameVersion (which allows you to make breaking changes). /// </summary> string gameVersion = "1"; #endregion #region MonoBehaviour CallBacks /// <summary> /// MonoBehaviour method called on GameObject by Unity during early initialization phase. /// </summary> void Awake() { // #Critical // this makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically PhotonNetwork.AutomaticallySyncScene = true; } /// <summary> /// MonoBehaviour method called on GameObject by Unity during initialization phase. /// </summary> void Start() { Connect(); } #endregion #region Public Methods /// <summary> /// Start the connection process. /// - If already connected, we attempt joining a random room /// - if not yet connected, Connect this application instance to Photon Cloud Network /// </summary> public void Connect() { // we check if we are connected or not, we join if we are , else we initiate the connection to the server. if (PhotonNetwork.IsConnected) { // #Critical we need at this point to attempt joining a Random Room. If it fails, we'll get notified in OnJoinRandomFailed() and we'll create one. PhotonNetwork.JoinRandomRoom(); } else { // #Critical, we must first and foremost connect to Photon Online Server. PhotonNetwork.GameVersion = gameVersion; PhotonNetwork.ConnectUsingSettings(); } } #endregion } }
C#スクリプト
Launcher
を保存します。
ここまでのスクリプトの内容を確認しましょう。まずは一般的なUnityの観点から、そして作成したPUN特有のコールを見ていきます。
Namespace:
必須ではありませんが、スクリプトに適切なnamespaceをあてがうことは他のアセットや開発者との競合を防ぎます。
他の開発者もクラス Launcher を作成していたらどうなるでしょうか?Unityは警告を出し、自分かその開発者はUnityが実行できるように名前を付けなおす必要があります。
競合がAsset Storeからダウンロードしたアセットから起こっている場合は注意が必要です。Launcher クラスは、実際は Com.MyCompany.MyGame.Launcher で、あなたが所有しているドメインを使用しています。このため、完全に同一のNamespaceを他の誰かが使用する可能性は非常に低いです。逆ドメイン規則をnamespaceとして使用することによって、安全に作業できます。Com.MyCompany.MyGame は独自の逆ドメイン名とゲーム名で置き換えられます。これは、従うべき良い慣習です。
MonoBehaviourクラス:
クラスをUnity Componentに変換してGameObjectまたは[プレハブ]にドロップできるようにするため、クラスをMonoBehaviourで派生していることに注目してください。
MonoBehaviourを拡張するクラスは、多くの非常に重要なメソッドやプロパティへのアクセスすることができます。
今回のケースではAwake()とStart()の2つのコールバックメソッドを使用します。PhotonNetwork.GameVersion:
gameVersion
変数がgameversionを表していることに注目してください。
すでに本番で稼働しているプロジェクトに互換性を破る変更を加える必要が生じるまでは、"1"
のままにしなければなりません。ここで重要なことは、このメソッドがPhoton Cloudへの接続の起点であるということです。
この機能がtrueである時、MasterClient はPhotonNetwork.LoadLevel()を呼び出すことができ、接続済みのプレイヤーは全員自動的に同じレベルを読み混むことになります。
この時点で、Launcherシーンを保存しPhotonServerSettingsを開くことができ、(Unityメニュー Window/Photon Unity Networking/Highlight Photon Server Settings から選択します。)PUNロギングを「Full」に設定します。
これでPlayを押すことができます。
Unityコンソールからログを確認することができます。
ここで気づいてほしいのが、「サーバーからUserIDを受信した」点です。
これだけでも、接続がうまくいったことの確認になります。少し後に他の通知も見ていきます。
Connect() to 'ns.exitgames.com' failed: System.Net.Sockets.SocketException: No such host is known.
スクリプトがこの問題を認識してスムーズに反応し、どのような問題や状況にも対応できるのが理想です。
それではこれらの両方のケースを処理し、実際にPUNサーバに接続されたかどうかがLauncher
スクリプト内で通知されるようにしましょう。
これはPUNコールバックの導入として最適です。
PUNコールバック
PUNはコールバックに対し非常に柔軟性が高く、2つの異なる実装を提供しています。
勉強のために全てのアプローチを説明します。状況に応じて最適な方法を選べるようにしましょう。
Callbackインターフェースの実装
PUNではクラスに実装できる以下のC#インターフェースを提供しています。
IConnectionCallbacks
: 接続関連のコールバックIInRoomCallbacks
: ルーム内で発生するコールバックILobbyCallbacks
: ロビー関連のコールバックIMatchmakingCallbacks
: マッチメイキング関連のコールバックIOnEventCallback
: 受信する全てのイベント用の単一コールバック。これはC#イベントOnEventReceived
と同等です。IWebRpcCallback
: 受信中のWebRPCの操作レスポンスに対する単一コールバックIPunInstantiateMagicCallback
: インスタンス化されたPUNプレハブの単一コールバックIPunObservable
: PhotonViewシリアライゼーションのコールバックIPunOwnershipCallbacks
: PUN所有権移行のコールバック
コールバックのインターフェースは登録および登録解除する必要があります。
PhotonNetwork.AddCallbackTarget(this)
およびPhotonNetwork.RemoveCallbackTarget(this)
を呼び出します。(おそらくそれぞれOnEnable()
およびOnDisable()
内です。)
これは、クラスがすべてのインターフェースにコンパイルしていることを確認しながら、開発者がすべてのインターフェース宣言を実装することを強制する非常に安全な方法です。
ほとんどの優良なIDEでこのタスクを容易におこなうことができます。
ただし、何もおこなわないにも関わらずUnityコンパイラのために実装しなければならないメソッドでスクリプトがいっぱいになる可能性があります。
これはスクリプトがすべて、またはほとんどのPUN機能を頻繁に使用する場合です。
ここではIPunObservable
を、このチュートリアルの下部でデータシリアライゼーション時に使用します。
MonoBehaviourPunCallbacksの拡張
たびたび使用していくもう片方の技術は、最も便利なものです。
MonoBehaviourに起因するクラスを派生する代わりに、MonoBehaviourPunCallbacksからクラスを派生します。特定のプロパティと仮想のメソッドを使用して オーバーライドを都合のいいときにできるようになります。
タイプミスがないことを確認でき、また全てのメソッドを実装する必要がないので、これは非常に便利です。
注意:オーバーライドする際、多くのIDEではデフォルトで基本のコールを実装し自動で埋めますが、このケースではその必要がありません。
MonoBehaviourPunCallbacksの一般的なルールとして OnEnable()
もしくはOnDisable()
をオーバーライドしない限り、ベースメソッドを呼び出さないでください。
OnEnable()
またはOnDisable()
をオーバーライドする際は、常にベースクラスメソッドを呼び出してください。
ではOnConnectedToMaster()およびOnDisconnected()のPUNコールバックを使って実際に実行してみましょう。
C#スクリプト
Launcher
を編集します。MonoBehaviourからMonoBehaviourPunCallbacksへベースクラスを変更します。
C#
public class Launcher : MonoBehaviourPunCallbacks {
クラス定義の前でファイルの上部に
using Photon.Realtime;
を追加しますわかりやすくするために、
MonoBehaviourPunCallbacks Callbacks
リージョン内のクラスの最後に以下の2つのメソッドを追加します。C#
#region MonoBehaviourPunCallbacks Callbacks public override void OnConnectedToMaster() { Debug.Log("PUN Basics Tutorial/Launcher: OnConnectedToMaster() was called by PUN"); } public override void OnDisconnected(DisconnectCause cause) { Debug.LogWarningFormat("PUN Basics Tutorial/Launcher: OnDisconnected() was called by PUN with reason {0}", cause); } #endregion
Launcher
スクリプトを保存します。
これでこのシーンをインターネット接続有もしくはなしの状態で再生してみると、プレイヤーに通知したりロジックを進行するための適切な措置をとることができます。
次のセクションでUIの構築を開始する際に説明します。
ここでは、接続を成功させることについて説明します。
OnConnectedToMaster()メソッドを以下呼び出しに追加します。
C#
// #Critical: The first we try to do is to join a potential existing room. If there is, good, else, we'll be called back with OnJoinRandomFailed()
PhotonNetwork.JoinRandomRoom();
コメントに記載されている通り、ルームへのランダムな参加が失敗した場合はその通知を受信する必要があります。その場合は実際にルームを作成しなければならないため、OnJoinRandomFailed()PUNコールバックをスクリプトに実装し、 PhotonNetwork.CreateRoom()を使用してルームを作成する必要がありますルームに参加したことをスクリプトに通知する、関連するOnJoinRandomFailed()PUNコールバックも必要です。
C#
public override void OnJoinRandomFailed(short returnCode, string message)
{
Debug.Log("PUN Basics Tutorial/Launcher:OnJoinRandomFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom");
// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.
PhotonNetwork.CreateRoom(null, new RoomOptions());
}
public override void OnJoinedRoom()
{
Debug.Log("PUN Basics Tutorial/Launcher: OnJoinedRoom() called by PUN. Now this client is in a room.");
}
これでシーンを実行すると、PUNへの接続ロジックに従うはずです。既存のルームへの参加を試みるか、新しくルームを作成してそれに参加します。
以上で、ルームへの接続および参加に関する重要な側面を説明しました。次に、対処が必要となる不便な点がいくつかあります。
これらはPUNの習得にはあまり関係ありませんが、全体的な観点からは重要です。
Unityインスペクターでフィールドを公開
ご存知かもしれませんが、MonoBehavioursは自動的にフィールドをUnityインスペクターに公開します。
デフォルトで [HideInInspector]
とマークされていない限り、Publicフィールドは全て公開されています。
Publicではないフィールドを公開する場合、 [SerializeField]
属性を使用できます。
これはUnityの中でもとても重要な概念で、今回のケースではルーム毎の最大プレイヤー数を変更しインスペクターに公開します。こうすることでコードそのものに触らずに設定できます。
ルーム毎の最大プレイヤー数についても同じように処理します。
ここをコード内でハードコードするのはベストプラクティスではありません。代わりに、後から決めて再コンパイルをせずに数字を試せるよう、パブリック変数にしましょう。
Private Serializable Fields
リージョン内でクラスの宣言の冒頭部分に以下を追加します。
C#
/// <summary>
/// The maximum number of players per room. When a room is full, it can't be joined by new players, and so new room will be created.
/// </summary>
[Tooltip("The maximum number of players per room. When a room is full, it can't be joined by new players, and so new room will be created")]
[SerializeField]
private byte maxPlayersPerRoom = 4;
そして、以前のようにハードコードした数字を使用するのではなく、PhotonNetwork.CreateRoom()コールを修正しこの新しいフィールドを使用します。
C#
// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.
PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = maxPlayersPerRoom });
これで、スクリプトに静的MaxPlayersの値の使用を強制することなく、Unityインスペクターで設定し、実行するだけでよくなります。スクリプトを開いたり、編集・保存したりUnityが再コンパイルしてやっと実行するのを待つ必要がなくなりました。
この方法を使うと格段に効率が良くなり、柔軟性があがります。