Photon Unity Networking  v1.67
基本説明

Table of Contents

Photon・サブスクリプション契約・サーバー種別・開始方法について手短かに主要点を説明します。

Photon

Unity内蔵のネットワークと異なり、Photon Unity Networking(PUN)は常に1つの専用サーバーに接続しています。そのサーバーはRoom、マッチメイキング、プレイヤー同士のRoom内コミュニケーション機能を提供します。内部的にはPUNは複数サーバーを使用しています。複数の「ゲームサーバー」では実際のRoom(対戦)を動かす一方、「マスターサーバー」は部屋と対戦プレイヤーを管理しているのです。

サーバーは2種類から選択することができます。

Exit Games Cloud

Exit Games Cloudはクラウドサービスです。Exit Games社が運用管理する、負荷分散されたPhoton Serverを皆様に提供しています。無料体験をご利用できます。また、商用利用のためのサブスクリプション費用 も他に負けない低価格です。

サービスは決められた一定のロジックで稼働しており、サーバー側のゲームロジックをユーザー自身で実装はできません。その代わり、クライアントに権限を持たせる必要があります。

クライアントは「アプリケーションID」で区別されます。そのIDはゲームのタイトルと「ゲームのバージョン」に関連付けられたものです。それによって、プレイヤーが他の開発者のゲームや過去のバージョンと競合しないようにしているのです。

Asset Storeで購入されたサブスクリプション

Photon CloudのサブスクリプションパッケージをAsset Storeで購入された場合、次の手順にしたがってください:

Photon Server SDK

Photon Cloudの代わりの選択肢として、自分でサーバーを立てて、我々の「負荷分散」C# ソリューションの上に、独自のサーバー側ロジックを開発することができます。この方法だとサーバーロジックをすべて制御できます。

Photon Server SDK ダウンロード: www.photonengine.com/ja/OnPremise/Download

サーバーを起動します: doc.photonenigne.com/ja/onpremise/current/getting-started/photon-server-in-5min

Photon Unity Networking 最初の一歩

PUNをインポートすると「ウィザード」ウィンドウがポップアップします。メールアドレスを登録するか、この段階をスキップして既存アカウントのアプリIDを入力するか、「自前サーバー」のPhotonに切り替えて自分のサーバーアドレスを入力するか、のいずれかを実行します。

これにより、クラウドサービス、または自前Photon Serverのサーバー設定がプロジェクト内で作られます: PhotonServerSettings

PUNは多数のファイルで構成されます。しかし重要なのはたった1つ: PhotonNetworkです。このクラスは必要とされるすべての関数と変数を含んでいます。さらに独自の要件がある場合、いつでもソースファイルを編集可能です。なにしろこのプラグインはPhotonの実装そのものなのですから。

もしUnityscriptを使っているなら、「Photon Unity Networking\Plugins」フォルダーをプロジェクトの一番上(root)に移動させてください。

このAPIがどのように動作するか、いくつかのサンプルを列挙します。

マスターサーバーとロビー

PUNは常に1つのマスターサーバーと1つ以上のゲームサーバーを使用します。マスターサーバーは、複数ゲームサーバーで進行中のゲームを管理します。また、ゲームサーバーのアドレスを、Room入室/作成時に提供します。(クライアントの)PUNは自動的にそのゲームサーバーに切り替えます。

個々の対戦(マッチ)はRoomと呼ばれます。Roomはそれぞれ独立し、名前で区別されます。Roomはグループにまとめられ、1つまたは複数のロビーに所属します。ロビーはマッチメイキングで任意の部分になります。明示的に独自ロビーを使用しなければ、PUNはすべてのRoomに対し1つのロビーを使用します。

初期設定では、PUNがデフォルトロビーに入るのは接続後です。このロビーは存在するRoom一覧をクライアントに送信します。(名前やプロパティを見て)プレイヤーは部屋を選ぶことができます。PhotonNetwork.GetRoomList()を使って現在の一覧にアクセスしましょう。一覧は間隔を空けて更新されますが、これは通信量を減らすためです。

クライアントはロビーに入ったりRoomに入室/Roomを作成する必要はありません。クライアントにRoom一覧を提示したくないなら、PhotonNetwork.autoJoinLobby = falseと指定してください。すると、クライアントはロビーをスキップできるようになります。

ゲームに必要なら、複数のロビーでRoom一覧を構成できます。PhotonNetwork.JoinLobbyメソッドで特定のロビーに入ることができます。クライアント側でそれを行い、サーバー側はそれを監視します。名前と型が同じ限り、TypedLobbyはどのクライアントにも同じになるでしょう。

1クライアントは常に1つのロビーに存在します。ロビーにいるときRoomを作成すると、Roomはそのロビーに結びつけられます。複数ロビーによって、クライアントはより短いRoom一覧を得られます。これは良いことです。Room一覧の長さに制限はないからです。

JoinRoom, JoinRandomRoom, CreateRoomのパラメーターを指定すると、ロビーに入らなくてもロビーを選ぶことができます。

プレイヤー同士はロビーにいるお互いを認識しませんし、データを送ることもできません(混雑時の問題を避けるためです)。

サーバーはすべて専用マシンで動作します。プレイヤーがホスト(運営)するサーバーなんてものはありません。サーバー構成を覚えておこうと悩む必要はありません。APIがその煩雑さを隠してくれるからです。

上記のコードはPhotonNetworkの機能を利用するため必ず必要となります。クライアントのゲームバージョンが設定され、セットアップウィザードの設定(PhotonServerSettingsに保存されています)を使用することになります。ウィザードは自前サーバーでPhotonを運用するのにも使われます。この代わりにConnect()を使うとPhotonServerSettingsファイル設定を無視できます。

バージョン管理

Photonの負荷分散ロジックは、アプリIDでプレイヤーを区別しています。ゲームバージョンも同じように、新しいクライアントと過去のクライアントを区別します。PUNのバージョンが異なると互換性を保証できないので、ゲームバージョンの後にPUNバージョンも送信しています。(PUN v1.7から)

Roomに入室、Roomを作成

次に、Roomに入室またはRoomを作成したいと思います。以下のコードで必要な機能をいくつか示します:

//Roomに入室
//Roomを作成.
//すでに存在していれば失敗し、これを呼ぶ: OnPhotonCreateGameFailed
//ランダムにどこかのRoomに入室:
//もしマッチするゲームがなければ失敗: OnPhotonRandomJoinFailed

現在動作中のゲーム一覧を、マスターサーバーのロビーが提供します。ロビーは他のRoomと同じく入ることはできますが、Room一覧の提供と更新しかやりません。PhotonNetworkプラグインでは、接続後ロビーに自動的に入ります。Roomに入室すると、Room一覧は更新されません。

(ロビーで)Room一覧を表示:

foreach (RoomInfo game in PhotonNetwork.GetRoomList())
{
GUILayout.Label(game.name + " " + game.playerCount + "/" + game.maxPlayers);
}

その他にもランダムなマッチメイキングがあります。どこかのRoomに入室しようとしますが、他のプレイヤーの入る空きがない場合失敗します。このとき、名無しの部屋を作成し、他のプレイヤーがランダムに入室してくるまで待ちます。

高度なマッチメイキングとRoomプロパティ

まったくのランダムなマッチメイキングはプレイヤーをいつも楽しませるものではありません。ときには特定のマップで遊びたいとか2人だけで対戦したくなるものです。

Photon Cloudと負荷分散では、任意のRoomプロパティを指定したり、フィルター条件とすることが、JoinRandomを使えば可能です。

Roomプロパティとロビー

Roomプロパティは、Roomにいるプレイヤーすべてで同期されます。それは現在のマップ、ラウンド、開始時間などを把握するのに役立ちます。文字列をキー(短いキーが望ましい)にしたハッシュテーブルとして扱います。

選択したプロパティをロビーに転送可能です。そうすると一覧に載り、ランダムマッチングにも使用されます。Roomプロパティのすべてがロビーで関心を引くわけではありません。そこでプロパティをまとめたものを定義して、Room作成時ロビーに指定できます。

Hashtable roomProps = new Hashtable() { { "map", 1 } };
string[] roomPropsInLobby = { "map", "ai" };
RoomOptions roomOptions = new RoomOptions() { customRoomProperties = roomProps, customRoomPropertiesForLobby = roomPropsInLobby }
CreateRoom(roomName, roomOptions, TypedLobby.Default)

注意していただきたいのは、"ai"はRoomプロパティのキーにはまだなっていない、ということです。ゲーム中にRoom.SetCustomProperties()で指定されるまでは、ロビーに公開されません。"map" か "ai"の値を変更すると、ロビーでも少し遅れて更新されます。

一覧を短く抑えましょう。そうすれば一覧読み込みの動作でパフォーマンスを低下させずにすみます。

RoomプロパティをJoin Randomでフィルタリング

JoinRandomで、想定されるRoomプロパティと最大プレイヤー値を含むHashtableを渡すことができます。サーバーが適合するRoomを選ぶとき、それはフィルターとして動作します。

Hashtable expectedCustomRoomProperties = new Hashtable() { { "map", 1 } };
JoinRandomRoom(expectedCustomRoomProperties, 4);

フィルター条件にするプロパティを多くすると、条件にあうRoomがある可能性は減ります。条件にするプロパティは限定したほうがよいでしょう。

ロビーが認識していないプロパティはフィルター条件にしないよう注意してください(上記参照)。

MonoBehaviourコールバック

PUNは何種類かのコールバックを使って、「接続済み connected」や「ゲームに参加中 joined a game」のような状態変化をゲームに知らせます。MonoBehaviourから適合するメソッドを実装して、イベントが起きたとき呼ばれるようにしましょう。

利用可能なコールバックをおおまかに見るには、Photon.PunBehaviourクラスを見ましょう。スクリプトを(MonoBehaviourの代わりに)PunBehaviourにしたのなら、個々のコールバックを簡単に上書き(override)できます。「override」とタイピングを始めるとIDEがコールバック一覧を示してくれるので、探すのは簡単です。

ここまでが、ゲームRoomのセットアップの基本です。次はゲーム内の実際のコミュニケーションについてです。

Room内でメッセージを送る

Room内で、接続している他プレイヤーにネットワークメッセージを送ることができます。バッファリングされたメッセージを送ることもできますが、それは後に接続してくるプレイヤーに送信されることになります(たとえば自分のプレイヤーを出現させる場合)。

メッセージ送信方法は2種類あります。RPCを使うか、またはPhotonViewのプロパティOnSerializePhotonViewを使うかです。ネットワークのやりとりはそれだけでは済みませんけどね。ある種のネットワークイベントはコールバックで監視できます(例:OnPhotonInstantiate, OnPhotonPlayerConnected)。そして何らかのイベント(たとえばPhotonNetwork.Instantiate)を発生させることができます。最後の説明がわからなくても気にしないでください。次にそれらの話題について説明します。

PUNでグループを使う

グループは、どのPhotonViewで変更されても同期しません。開発者次第ですが、必要なら、全クライアントでPhotonViewを同じグループに固定できます。別々のクライアントで、同じPhotonView、別のグループ番号を使うと 一貫性のない動作を引き起こすことになります。

一部のネットワークメッセージは、受信対象グループを確認されるのは、受信側だけです。名前を挙げると:

技術的な理由: Photon Serverがサポートしているのは、キャッシュされず、特定のプレイヤー向けでないメッセージに対するインタレストグループだけです。この挙動は将来変更されるかもしれません。

PhotonView

PhotonViewはスクリプトコンポーネントです。メッセージ送信(RPCとOnSerializePhotonView)に使われます。PhotonViewはゲームのGameObjectに追加する必要があります。PhotonViewはUnityのNetworkViewにとても似ています。

常にゲームに少なくとも1つのPhotonViewが必要です。メッセージ送信のため、そして任意に他のPhotonViewからインスタンス作成/割り当てのためです。

PhotonViewをGameObjectに追加するには、単にGameObjectを選択して“Components/Miscellaneous/Photon View”を使います。

doc-photonview.jpg
Photon View

Transformを監視

PhotonViewのObserveプロパティにTransformを追加するとき、Position(位置)・Rotation(回転)・Scale(縮尺)とその組み合わせを、プレイヤー間で同期させるのかを選択できます。プロトタイピングや小規模なゲームを作るときに有用です。注意:監視する値がどれか変更されたら、変更された1つの値でなく、監視するすべての値が送信されます。更新データは平滑化も補間もされません。

MonoBehaviourを監視

MonoBehaviourを監視するよう、PhotonViewを設定できます。その場合、スクリプトのOnPhotonSerializeViewメソッドが呼ばれます。呼ばれたとき、オブジェクトの状態を書き出すか読み込むかは、スクリプトをローカルプレイヤーがコントロールしているか否かで変わります。

以下の簡単なコードは、あと数行コードを書くことで、キャラクターの状態を同期させます:

{
if (stream.isWriting)
{
//このプレイヤーを所有している: 他プレイヤーにデータを送信
stream.SendNext((int)controllerScript._characterState);
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}
else
{
//ネットワークプレイヤー, データを受信
controllerScript._characterState = (CharacterState)(int)stream.ReceiveNext();
correctPlayerPos = (Vector3)stream.ReceiveNext();
correctPlayerRot = (Quaternion)stream.ReceiveNext();
}
}

送信するとき“ReliableDeltaCompressed”モードなら、常に同じ順序でデータをストリームに書き込んでください。PhotonStreamに書き込みがないと、更新情報は送信されません。これは一時停止のとき便利な挙動です。 さてそれでは、もう1つのコミュニケーション方法、RPCを見てみましょう。

リモートプロシージャーコール

Remote Procedure Calls (RPCs)は名前が示すとおり、同じRoomのリモートクライアントが呼ぶメソッドです。MonoBehaviourのメソッドを、リモートコールとして使うには [PunRPC] 属性を適用する必要があります。マーキングした関数を呼ぶには、同じGameObjectにPhotonViewインスタンスが必要です。

void ChatMessage(string a, string b)
{
Debug.Log("ChatMessage " + a + " " + b);
}

スクリプトからメソッドを呼ぶには、PhotonViewオブジェクトにアクセスする必要があります。スクリプトがPhoton.MonoBehaviourを継承していれば、photonViewフィールドがあります。通常のMonoBehaviourか、GameObjectであれば、PhotonView.Get(this)を使ってPhotonViewコンポーネントへアクセスして、その上でRPCを呼び出します。

PhotonView photonView = PhotonView.Get(this);
photonView.RPC("ChatMessage", PhotonTargets.All, "jup", "and jup!");

このように対象メソッドを直接呼び出す代わりに、PhotonViewでRPC()を呼び出します。呼び出すメソッド名とそれを呼び出すプレイヤーを指定してください。その次にパラメーター一覧を指定します。

注意: RPCで使うパラメーター一覧は、想定されるパラメーターの数と合致していなくてはなりません。もし受信側のクライアントに一致するメソッドを見つけられない場合は、エラーを記録します。 このルールには1つ例外があり、RPCメソッドの最後のパラメーターはPhotonMessageInfo型であってもよいのです。これはそれぞれのコールにちょっとした文脈情報を与えます。

void ChatMessage(string a, string b, PhotonMessageInfo info)
{
Debug.Log(String.Format("Info: {0} {1} {2}", info.sender, info.photonView, info.timestamp));
}

RPCのタイミングとレベル読み込み

RPCは特定のPhotonViewで呼ばれ、リモートクライアント上の合致するPhotonViewを常にターゲットとします。リモートクライアントの合致するPhotonViewfが不明ならRPCは失われます。

RPCを失う理由でよくあるのは、クライアントがレベルを読み込んでセットアップしている最中だったということです。つまり、クライアントの1つが、他より速いか、Roomに長く滞在して、他のクライアントではまだ読み込み完了していないオブジェクトに重要なRPCを送信してしまったときです。同じことはRPCがバッファリングされているときにも起こります。

解決策は、シーン読み込みしているあいだはメッセージキューを一時停止しておくことです。以下のコードはその方法を示します:

private IEnumerator MoveToGameScene()
{
// 以降のネットワークメッセージを一時的に停止
Application.LoadLevel(levelName);
}

代わりの方法として、PhotonNetwork.LoadLevelを使うこともできます。これは一時的にメッセージキューを使用不可にします。

メッセージキューを使用不可にすると、キューを再開するまでは、メッセージの送受信を遅延させます。当たり前ですが、準備ができたときキューを再開するのはとても重要です。

前回ロードしたシーンに属するRPCで、シーン変更後遅れて到着したものは、破棄されます。これは逆に、RPCでシーン間の区切りを定義できるということです。

その他のトピック

Unity Networkingとの違い

  1. ホスティングモデル
    • Unity Networkingはサーバー・クライアントベースです(P2Pではありません)。サーバーはUnityクライアント1台で(つまりプレイヤー1人により)稼働します。
    • Photonもサーバー・クライアントベースですが、専用サーバーを使います。ホスト(をこなすプレイヤー)が離脱しても接続が中断されることはもうありません。
  2. 接続性
    • Unity NetworkingはNATパンチスルーを利用して接続性を向上させています。というのもプレイヤーがネットワークサーバーをホストするので、ファイアウォールやルーター等により接続によく失敗するためです。接続成功率は低く、接続性は保証されません。
    • Photonは専用サーバーを使うので、NATパンチスルーなどの方法は必要ありません。接続性は100を保証されています。まれに接続が失敗することもあり得ますが、それはクライアント側ネットワークの厳しい制約(たとえば企業内VPN)のためです。
  3. パフォーマンス
    • Photonはパフォーマンス面で、Unity Networkingを凌駕しています。それを証明する数値はまだありませんが、これまで何年にわたりライブラリは最適化されてきています。さらに、Unityサーバーはプレイヤーがホストしているため、遅延時間/pingは一般的によくありません。サーバーを稼働させているプレイヤーの接続回線に頼っているためです。この回線が、専用のPhotonサーバー接続回線よりよくなることはありません。
  4. 価格
    • Unity Networkingと同じように、Photon Unity Networking(PUN)プラグインも無料です。サブスクリプション契約でPhoton Cloudのホスティングサービスを使うことができます。もしくは、自前のサーバーを借りて、そこでPhotonを稼働させることもできます。無料ライセンスで同時接続100プレイヤーまで可能です。他のライセンスは(ホスティングするときに)一括料金が発生し、同時接続ユーザー数を増やせます。
  5. 機能とメンテナンス
    • Unityはネットワークの実装をそれほど重要視していないようです。機能追加はほとんどなく、バグフィックスもめったにありません。Photonの場合は活発にメンテナンスされていて、多くがソースコードで提供されています。その上、PhotonはすでにUnityより多くの機能を提供しているのです。たとえば、組み込みロードバランサーや、オフラインモードなどです。
  6. マスターサーバー
    • Photonのマスターサーバーは、通常のUnity Networkingにおけるマスターサーバーとは少し違います。Photonの場合、Photon Serverが、「ロビー」でプレイ中のゲームのRoom名を一覧表示します。そのサーバーは、Unityのマスターサーバーと同じように、クライアントを実際にゲームプレイ中のゲームサーバーに転送します。

ネットワーク上のオブジェクトからインスタンスを作成する

どんなゲームも、プレイヤーに対して、1つ以上のプレイヤーオブジェクトからインスタンスを作成する必要があります。その方法をいくつか以下にリストアップしました。

PhotonNetwork.Instantiate

PUNでは、オブジェクトの生成を自動的に管理できます。開始位置・回転・プレハブ名をPhotonNetwork.Instantiateメソッドに自動的に渡してくれるのです。 必要条件: プレハブはResourcesフォルダーの直下に配置する必要があります。そうすることでプレハブは実行時に読み込まれます。 Web Playerに注意してください。初期設定では、Resourcesフォルダーにあるすべてのものは、ストリーミングの最初のシーンに読み込まれます。Web Player設定で「First streamed level」を指定すると、最初のレベルで使用するResourcesフォルダーのアセットを指定できます。最初のゲームシーンでこれをセットして、Resourcesフォルダーのアセットを使わないなら、プリローダーとメインメニューが遅くならずにすむでしょう。

void SpawnMyPlayerEverywhere()
{
PhotonNetwork.Instantiate(“MyPrefabName”, new Vector3(0,0,0), Quaternion.identity, 0);
//最後の引数はオプションで、グループ番号です。今は無視してかまいません。
}

より詳細なコントロール:手動でインスタンス作成

ネットワーク上のオブジェクトからインスタンスを作成するとき、Resourcesフォルダーに依存したくない場合、手動でInstantiate(インスタンスを作成)する必要が生じます。このセクションの最後に例があります。

手動でインスタンス作成する主な理由は、Web Playerのストリーミングのために、どれがいつダウンロードされるかコントロールするためです。ストリーミングとUnityのResourcesフォルダーについて詳細はこちらにあります。

手動で生成するなら、PhotonViewIDの割り当ても自分でする必要があります。viewIDはゲームオブジェクトやスクリプトに、正しくネットワークメッセージを伝達するカギとなります。プレイヤーは、新しいオブジェクトを所有・生成したいユーザーは、PhotonNetwork.AllocateViewID()を使って新しいviewIDを割り当てなくてはなりません。このPhotonViewIDは、他のすべての設定済みPhotonView(たとえば現存するシーンのPhotonView)を使っているプレイヤーに送信されます。ここで気をつけていただきたいですが、このRPCはバッファリングされる必要があるということです。そうすることで、後から接続するクライアントも生成指示情報を受信できます。オブジェクト生成に使うRPCメッセージは、目的とするプレハブへの参照を必要としていて、インスタンス作成のためにUnityのGameObject.Instantiateを使います。最後に、そのプレハブに追加されているPhotonViewを設定する必要があります。PhotonViewすべてにPhotonViewIDを割り当てる設定です。

void SpawnMyPlayerEverywhere()
{
//手動でPhotonViewIDを割り当て
PhotonViewID id1 = PhotonNetwork.AllocateViewID();
photonView.RPC("SpawnOnNetwork", PhotonTargets.AllBuffered, transform.position,
transform.rotation, id1, PhotonNetwork.player);
}
public Transform playerPrefab; //set this in the inspector
void SpawnOnNetwork(Vector3 pos, Quaternion rot, PhotonViewID id1, PhotonPlayer np)
{
Transform newPlayer = Instantiate(playerPrefab, pos, rot) as Transform;
//PhotonViewを設定
PhotonView[] nViews = go.GetComponentsInChildren<PhotonView>();
nViews[0].viewID = id1;
}

Asset Bundleを使ってネットワークオブジェクトを読み込みたい場合、コードを読み込む自分のAsset Bundleを追加して、サンプルにある"playerPrefab"をそのAsset Bundleのプレハブと置き換えるだけで可能です。

オフラインモード

オフラインモードは、マルチプレイヤー向けコードを、シングルプレイヤーゲームでも再利用できる機能です。

Mike Hergaarden氏のコメント: M2H社で、我々は何度もゲームを作り直すはめになったよ‥‥ゲームポータルでは通常、マルチプレイヤー機能を完全に削除することを求められるからね。その必要がなくなるだけでなく、シングルとマルチプレイヤーで同じコードを使えるということは、かなり作業量を減らせるということさ。

シングルプレイヤーでも使いたい機能として最もよく知られているのは、RPCを送信してPhotonNetwork.Instantiateメソッドを使うことです。オフラインモードの主な目標は、非接続状態でPhotonNetworkの機能を使っても、参照不能や他のエラーを起こさないようにすることです。もちろん、シングルプレイヤーゲーム起動中だと把握する処理や、ゲームのセットアップなどは必要になります。しかし、ゲームを起動中であれば、すべてのコードは再利用可能になります。

オフラインモードを使うには手動で設定する必要があります。この設定は、PhotonNetworkがエラーと意図した動作を区別できるようにするため必要です。この設定は簡単です:

これで、特定のマルチプレイヤー向けメソッドを、接続せずエラーを引き起こすことなく、再利用できるのです。以下は、オフラインモード時のPhotonネットワークの関数と変数とその結果の一覧です:

PhotonNetwork.player プレイヤーIDは常に-1です。 PhotonNetwork.playerName 期待通り動作します。 PhotonNetwork.playerList ローカルプレイヤーのみを含みます。 PhotonNetwork.otherPlayers 常に空です。 PhotonNetwork.time Time.timeを返します。 PhotonNetwork.isMasterClient 常にtrueです。 PhotonNetwork.AllocateViewID() 期待通り動作します。 PhotonNetwork.Instantiate W期待通り動作します。 PhotonNetwork.Destroy 期待通り動作します。 PhotonNetwork.RemoveRPCs/RemoveRPCsInGroup/SetReceivingEnabled/SetSendingEnabled/SetLevelPrefix シングルプレイヤーのとき何の意味もありませんが、しかしとくに悪さもしません。 PhotonView.RPC 期待通り動作します。

注意するべきは、上記以外のメソッドを使った場合、予期できない結果を引き起こしたり、単に何も起きなかったりするということです。たとえばPhotonNetwork.roomは明らかにnullを返します。もしゲーム開始時はシングルプレイヤーで、後のステージでマルチプレイヤーに移行したい場合、オンラインモードを使用する代わりに1プレイヤーゲームをホストしたほうがよいでしょう。そうすると、バッファリングされたRPCとInstantiation(インスタンス作成)呼び出しが保存されます。それに対し、オフラインモードではInstantiationは接続後に引き継がれません。

オフラインモードを停止するには、PhotonNetwork.offlineMode = false; と指定するか、または単にConnect()メソッドを呼び出しましょう。

制限事項

Viewとプレイヤー

パフォーマンス上の理由で、PhotonNetwork APIがサポートするPhotonViewはプレイヤーあたり1000個まで、プレイヤー数は2,147,483人までです(これはハードウェアのサポート限界よりずっと多いのです!)。プレイヤーの最大数を減らすことで、プレイヤーあたりのPhotonViewの最大値を簡単にふやすことができます。その仕組みはこうです: PhotonViewは、どのネットワークメッセージにもviewIDを送信します。このviewIDはint(整数)型で、プレイヤーIDとプレイヤーViewIDから構成されています。int型の最大数は2,147,483,647で、MAX_VIEW_IDS(1000)で割ると、200万以上のプレイヤーがそれぞれ1000のviewIDを持てることになります。このとおり、プレイヤー数をふやすには、MAX_VIEW_IDSを減らせばよいのです。また逆に、プレイヤー最大数を減らして個々のプレイヤーにもっと多くのVIEW_IDSを与えることもできます。 重要なことですが、ほとんどのゲームでは、プレイヤーあたりのviewIDを2,3個より増やす必要はないのです。(キャラクターあたり1か2が普通です)。増やす必要があるように思えたら、何かおかしなことをしているかもしれません。 武器から射出される弾丸すべてにPhotonViewとIDを振るのはきわめて非効率です。その代わりに、プレイヤーのPhotonView、または武器のPhotonViewを通して、発射した弾丸を管理するようにしてください。

回線帯域幅のパフォーマンスを向上させるため、int型からshort型(値域:−32,768から32,768)に減らす方法があります。MAX_VIEW_IDSを32に指定しても、まだ1023人のプレイヤーをサポートできます。int型viewIDの詳細は「//LIMITS NETWORKVIEWS&PLAYERS (NETWORKVIEWとプレイヤーを制限)」と検索してみてください。さらに言うと、現在のところAPIは uint型(マイナスなし整数)/ushort型を使っていませんが、プラスの値だけ使用しています。これはわかりやすさのためにやっています。また、viewIDをどう使用したところで、パフォーマンス問題にはほとんど大きな影響を及ぼしません。

グループとスコープ

PhotonNetworkプラグインはネットワーク・グループを完全にはサポートしていません。上記の「PUNでグループを使用する」を見てください。

Unityでの「スコープ」機能はまだ実装されていません。

フィードバック

我々は皆様のフィードバックに関心をもっております。このソリューションは進行中のプロジェクトなのですから。 もし何か開示されていない・欠落している・動作しないなどがありましたら、我々にどうぞ知らせてください。 お知らせいただくには、フォーラムに投稿をお願いします: forum.exitgames.com

よくある質問

1つのGameObject(ゲームオブジェクト)に複数のPhotonViewsを使用できますか? できるなら、それはなぜですか?

はい。それは問題ありません。2個以上のターゲットを監視する必要があるとき、複数のPhotonViewは必要になります。つまり、1つのPhotonViewは1つのターゲットしか監視できないのです。RPCにさえPhotonViewが1つ必要ですが、この場合何かをすでに監視しているのと同じPhotonViewで大丈夫です。RPCは、監視中ターゲットと競合することはありません。

UnityScript / Javascriptを使えますか?

UnityScriptでPhotonクラスを使えるようにするには、「Photon Unity Networking/Plugins」にあるPluginsフォルダーをプロジェクトのトップ(root)に移動して、自分のコードより先にコンパイルされるようにしてください。

Unity Networkingを使ったプロジェクトをPhotonに変換する

Unity Networkingを使ったプロジェクトをPhotonに変換するのは1日あればできます。念のため、プロジェクトをバックアップしておいてください。自動変換機能があなたのスクリプトを書き換えてしまうからです。それがすんだら、Photonエディターウィンドウで自動変換機能を起動してください (Window -> Photon Unity Networking -> Converter -> Start)。自動変換にかかる時間は、プロジェクトの規模とコンピュータの性能に左右されますが、30秒から10分間のあいだです。自動変換は次の処理をします:

小さな違いがいくつかあるので、スクリプト変換バグを少数、手動で修正する必要があります。 変換後、コンパイルがエラーすることがあります。まずそれを最初に修正しましょう。よくあるエラーといえば:

PhotonNetwork.RemoveRPCs(player); PhotonNetwork.DestroyPlayerObjects(player); これらは存在しないので、安全に削除できます。Photonは終了するときにプレイヤーを自動的にクリーンアップします。(必要なら、この機能を無効にして、自分の手で注意深くクリーンアップすることもできます。)

..CloseConnection takes ‘2' arguments… このコールの2つめの引数(boolean型)を削除してください。

PhotonNetwork.GetPing(player);

GetPingは引数をとりません。pingできるのはPhoton Serverに対してだけで、他のプレイヤーに対してはできません。

myPlayerClass.transform.photonView.XXX error このようなコードは myPlayerClass.transform.GetComponent<PhotonView>().XXX に変換する必要があります。スクリプト内でPhotonViewを使えば、PhotonViewコンポーネントを取得(get component)できますが、外部のtransformからは直接は呼べません。

RegisterServer マスターサーバーに対しゲームを登録する必要はありません。Photonが自動的に実行します。

コンパイルエラーはすべて、5~30分ほどあれば修正できるはずです。ほとんどのエラーはメインメニュー/GUIのコードから生じます。これは IP/Port/ロビーGUIに関連しています。

Photonが他のほとんどのUnityのソリューションと異なる点は:

Photon Serverは1つだけあり、それに接続するのにRoom名を使います。そのため、IP/Portへの参照をコード(一般的にGUIのコード)から削除できるのです。PhotonNetwork.JoinRoom(string room)メソッドはRoomだけを引数にとるので、以前の IP/Port/NAT引数は削除する必要があるでしょう。もしM2H社による“Ultimate Unity networking project”を使っているなら、MultiplayerFunctionsクラスを削除しましょう。

最後に、以前のMasterServerコールは削除できます。サーバー登録が必要になることはもうありません。Room一覧を取得するのはPhotonNetwork.GetRoomList()を呼び出すだけという簡単さです。そして、この一覧は常に更新されます(fetch/poll等は必要ありません)。ルーム一覧取得部分を書き換えるのが、作業のほとんどになるでしょう。GUIをやり直す必要があるなら、ゼロからGUIを書き直したほうが簡単かもしれません。

Online Documentation  -  Dashboard  -  Support Forum Exit Games GmbH