.NETプラットフォームSDK

本項はNETおよびUnity向けの、Photon Client Libraryのドキュメントとリファレンスです。

目次

概要

Photonは、様々なプラットフォーム向けにリアルタイムのマルチプレイヤーゲームやアプリケーションを構築するための開発フレームワークです。 様々なプラットフォームに対応するServer SDKとClient SDKで構成されています。 PhotonはUDP(またはTCP)にもとづく、低レイテンシーなコミュニケーションレイヤーを提供します。 このレイヤーによって、データの高信頼/低信頼転送を「コマンド」で実施できるようになります。

さらに、オペレーションフレームワークとイベントフレームワークが確立され、ゲーム開発の負担が減少します。

Photon Cloudは、Photonで開発したゲーム用にホスティングされるサービスです。 使用するにはまず、https://www.photonengine.comからサインアップします。その後LoadBalancing APIやデモを使って開発を始められます。

目次へ戻る

Photon ワークフロー

クライアントでの動作を理解するため、サーバーのLoadBalancing APIロジックを使用して説明します。 このアプリケーションは、ユーザーが参加を試みた場合に作成されるルームを定義します。 ルーム内の各ユーザーは、固有の番号をもつアクターとなります。

クライアントのワークフローを単純化すると、以下のようになります:

  • LoadBalancingClientインスタンスを作成
  • この時点から: イベントとコマンドを取得するため、定期的にServiceを呼びます (たとえば1秒間に10回)
  • Connectメソッドの1つを呼び、サーバーに接続します
  • ライブラリがOnStatusChangedを呼ぶまで待ちます
  • 返されたステータスintは、StatusCode.Connectと等しくする必要があります。
  • ゲームに参加するためOpJoinRoomを呼びます
  • ライブラリがOperationCode.JoinOnOperationResponseを呼ぶまで待ちます
  • OpRaiseEventを呼んで、ゲームにデータを送信します
  • OnEventにイベントを受信します
  • LoadBalancing APIは、よく発生する状況下で活用できるよう複数のイベントを定義しています: たとえば、プレイヤーの参加や退出などです。
  • LoadBalancing APIではOpRaiseEventを呼んで作成されるイベントは、同じルームにいる他のプレイヤーにこのメソッドで受信されます。
  • 完了すると: LoadBalancingClient.OpLeaveを呼んでゲームを停止するか、ゲームから退出します。
  • OperationCode.Leaveで、OnOperationResponse内で「leave」が返されるまで待ってください。
  • Disconnectで切断します
  • statusCode: StatusCode.DisconnectOnStatusChangedに「disconnect」が返されることを確認してください。 この簡潔なワークフローをサーバーのLoadBalancingアプリケーションと組み合わせれば、ルームの使用やゲームイベントの送信が可能になります。

使用されるメソッドは3つのレイヤーに分類できます: - 低レベル: Service、Connect、DisconnectOnStatusChangedは、サーバーへの接続を直接意味しています。 このレベルはコマンドを移送するUDP/TCPパケットとともに作動します(次に、あなたのオペレーションを転送します)。 これによって接続が維持され、RPC呼び出しやイベントがパッケージに管理されます。 - ロジックレベル: オペレーションや結果、イベントによってPhotonのロジックレベルが成り立っています。 すべてのオペレーションはサーバー上で定義され(たとえばRPC呼び出しなど)、結果が生じます。 イベントはサーバーから受信され、同じデータでクライアントをアップデートします。 - アプリケーションレベル: 特定のアプリケーションとその機能で成り立っています。 この場合、オペレーションとLoadBalancing APIのロジックが使用されます。 この特殊なケースにはルームやアクターなどが含まれます。

LoadBalancingClientはサーバー側の実装をマッチングし、すべてを包括しています。 多くの場合、低レベルの通信を管理する必要はありません。 ただしクライアントからサーバーへの通信(また、その逆も)は「コマンド」化される点に留意してください。 内部的には、コマンドはクライアントとサーバー間の接続を維持するのにも使用されます(追加データを保持することはありません)。

オペレーションであるメソッド(RPCコール)にはすべて頭に「Op」がつき、ほかのコールから区別されます。 他のサーバー側アプリケーション(MMOやユーザーが独自のアプリケーション)は異なるオペレーションを定義します。

これらは異なるパラメータを保持し、値を返します。 これらのオペレーションはクライアントライブラリの一部ではありませんが、OpCustomを呼ぶことで実装可能です。 コールバック用にインターフェースIPhotonPeerListenerを実装する必要があります。 以下に例を示します:

  • OnStatusChangedはピアのステート変更に使用します(接続、切断、エラー、StatusCodeのリストと比較してください)
  • OnOperationResponseはオペレーション用のコールバックです(参加、退出など)
  • OnEventは受信イベントのコールバックです
  • DebugReturnはデバッグ出力用のコールバックです (リリースビルドによって使用されますが、使用頻度は低いです) PhotonPeer内の下記のプロパティは特別な用途があります:
  • TimePingIntervalはpingオペレーションの時間間隔を設定します
  • RoundTripTimeは、高信頼性オペレーションがサーバーに送信されてから戻るまでのラウンドトリップタイムです
  • RoundTripTimeVarianceは上記のラウンドトリップタイムの変動を示します
  • ServerTimeInMilliSecondsは予測されるサーバーの継続時間です

目次へ戻る

オペレーション

オペレーションはPhotonでのリモートプロシージャーコール(RPC)を示す用語です。

これはサーバーサイドに実装されるメソッドで、クライアントに呼び出されます。

他のメソッドと同様に、オペレーションにはパラメータと戻り値があります。 Photon開発フレームワークはクライアントからサーバーへのRPCコールを考慮し、結果を返します。 サーバーサイドでは、オペレーションはPhotonの最上部で実行されるアプリケーションの一部です。 弊社が提供するデフォルトのアプリケーションは、「LoadBalancing API」と呼ばれます。 「LoadBalancingPeer」クラスは、各LoadBalancing APIオペレーション向けのメソッドによってPhotonPeerを拡張します。 LoadBalancingClientLoadBalancingPeerクラスのラッパーです。

LaodBalancing APIオペレーションの例として「Join」と「RaiseEvent」があります。 クライアント側では、これらのオペレーションはLoadBalancingClientクラス内にメソッドとして存在しています:OpJoinRoomOpRaiseEvent`です。 これらのオペレーションは、PhotonとLoadBalancing APIのデフォルトの実装でただちに使用することが可能です。

目次へ戻る

カスタムオペレーション

Photonは、それぞれのゲーム固有の機能に合わせて拡張できます。 ワールドステートを保持したり、クライアントからの情報をダブルチェックすることなどが可能です。

Lite、LoadBalancing API、MMOアプリケーションロジックに含まれないオペレーションはすべて、カスタムオペレーションと呼ばれます。 これらのオペレーションの作成はサーバーサイドのタスクですが、クライアントがサーバーの新しい機能やオペレーションを使用しなければならない場合もあります。

つまり、オペレーションはクライアント側から呼び出すことのできるメソッドです。 オペレーションには任意の数のパラメータを設定でき、また任意の名前をつけられます。 トラフィックを保持するため、弊社はすべてのオペレーションとパラメータにバイトコードを割り当てました。 定義はサーバー側で実行されます。 各オペレーションには、オペレーションコード(OperationCode)とよばれる固有の識別番号があります。 オペレーションクラスは、予測されるパラメータを定義し、それぞれにパラメータコードを割り当てます。 この定義によってクライアント側で必要な処理は、値を設定する点とオペレーションのOperationCodeであるとサーバーに通知する点のみになります。 Photonはオペレーションの要求、応答、イベントのパラメータを集約するため、ディクショナリを使用しています。

パラメータがディクショナリにあって、オペレーションを呼ぶ場合にはOpCustomを使用してください。

クライアント側では、現在OperationCodeとパラメータコードはバイト型です(オーバーヘッドを最小化するため)。 オペレーションを正常に呼び出すには、サーバー側の定義と合致させる必要があります。

詳細はこちらを参照してください:

目次へ戻る

イベント

オペレーションとは異なり、イベントは受信側のクライアントがめったにトリガーすることのない「メッセージ」です。 イベントは外部から送信されます:サーバーまたはその他のクライアントです。 イベントはオペレーションから副次的に作成されるか(たとえばルームに参加する場合など)、もしくはオペレーションRaise Eventの主目的として起動されます。

ほとんどのイベントはなんらかの形式のデータを転送しますが、まれにイベントの型自体がメッセージの場合があります。 (繰り返しになりますが)イベントは、任意のコンテンツのディクショナリです。 イベントの「最上位」では、バイトは値のキーとして使用されます。 シリアル化された型ならば、値はどのようなものでも問題ありません。 たとえばLoadBalancing APIは、オペレーションRaiseEvent内のカスタムイベントコンテンツの Hashtableを使用します。

詳細はこちらを参照してください:

目次へ戻る

フラグメンテーションとチャネル

フラグメンテーション

大きなサイズのデータチャンクは、1つのパッケージにはおさまりません。このため、こういったデータチャンクは分割されて自動的に再構成されます。 データサイズに応じて、1つのオペレーションやイベントが複数のパッケージで構成される場合があります。

この処理によって、その他のコマンドが遅延する可能性があります。 ServiceまたはSendOutgoingCommandsを、必要以上に頻繁に呼び出してください。 PhotonPeer.QueuedOutgoingCommandsがゼロになっている点を定期的に確認し、すべてが確実に出力されるようにする必要があります。

時々発生するデバッグ出力「UDP package is full」を確認することも可能ですが、この現象が常に発生しているのは問題です。

目次へ戻る

最大転送ユニット

UDPパッケージの最大サイズは、PhotonPeer.MaximumTransferUnitプロパティで設定できます。

デフォルト値は1,200バイトです。 ルーターによっては、UDPパッケージサイズがこのサイズでも分割をおこないます。 大きなサイズが不要な場合はパッケージを512バイトに設定してください。この場合、コマンドごとのオーバーヘッドは増しますが、安全性は 高まります。

この設定はTCP接続によって無視され、内部的にMTUが調整されます。

目次へ戻る

シーケンシング

プロトコルをシーケンシングする際には、受信クライアントは送信された順にアクションを送信します。 信頼性の低いデータは交換可能とみなされ、失われる可能性があります。 信頼性の高いイベントとオペレーションは必要に応じて複数回繰り返されますが、これらはすべて順番に間隔なしで送信されます。 信頼性の低いアクションは直近の信頼性の高いアクションに関連づけられ、信頼性の高いデータが送信されるまでは送信されません。 これは互いに関連したイベントの場合に有用です。

例:あなたのFPSが信頼性の低い移動アップデートと、信頼性の高いチャットメッセージを送信したとします。 移動アップデートのパッケージは失われて省略され、次の移動アップデートが受信されます。 受信側では、この処理は小さなジャンプのように見えます。 チャットメッセージをともなうパッケージが失われた場合、この処理は繰り返されます。メッセージ作成後のすべての移動アップデートに対してラグが発生する場合もあります。 この場合にはデータは関連づけられず、異なるチャネルに設定する必要があります。

目次へ戻る

チャネル

.NETクライアントとサーバーは、現在「チャネル」をサポートしています。 これによって、情報が複数のチャネルに分割され、これらのチャネルは個別にシーケンス化されます。 つまり、別のチャネルのイベントが利用できないという理由でチャネルのイベントが遅延することはありません。 デフォルトではPhotonPeerに2つのチャネルがあり、またチャネル0がオペレーションの送信をおこないます。 参加オペレーションと退出オペレーションは、常にチャネル0で送信されます(簡易化のため)。 メッセージの接続と切断には、内部的に使用される「バックグラウンド」チャネル255があります。 これはチャネルカウントでは無視されます。 チャネルには優先順位があります:まず、もっとも低いチャネル番号がUDPパッケージに入力されます。 UDPパッケージがすでにフルの場合には、より高い番号のチャネル内のデータは後で送信される可能性があります。

例:現状ではチャットメッセージはチャネル1で、移動はチャネル0で送信されるとします。 これらには関連性がなく、チャットメッセージが遅延した場合でも、チャネル0への影響はまったくありません。 また、チャネル0の方が優先順位が高いため、送信される可能性はより高くなります(パッケージがフルになった場合)。

目次へ戻る

TCPの使用

PhotonPeerは、必要に応じて下位層プロトコルとしてTCPでインスタンス化することができます。 最適な使用法ではありませんが、プラットフォームによってはUDPソケットをサポートしない場合があります。 たとえばSilverlightが常にTCPを使用するのはこのためです。 Photon Client APIは両方のプロトコルにとって同一ですが、内部的には若干の違いがあります。 低信頼と思われるオペレーションであっても、TCPで送信されるものはすべて高信頼です。 TCPクライアントのみを使用している場合には、すべてのオペレーションを低信頼性として送信してください。 これによって、下位層プロトコルでの一部の作業(およびトラフィック)を省略できます。 TCPクライアントとUDPクライアントが混在している場合には、TCPクライアント間で送信されるものは常に高信頼性として送信されます。 ただしUDPを使用するクライアントと通信する場合には、これらのクライアントはイベントを高信頼性または低信頼性として受信します。

例: Silverlighクライアントが、チャネル1で低信頼性の移動アップデートを送信するとします。 TCP経由で送信されるため、このアップデートは高信頼性になります。 PhotonはUDPクライアントとも接続しています(たとえば、ダウンロード可能な3Dのゲームクライアント)。 この場合、移動アップデートの送信には低信頼性/高信頼性の設定が使用されます。

目次へ戻る

ネットワークシミュレーション

開発中、テストの多くはローカルネットワーク上で行われます。 リリース後にクライアントはインターネット経由で通信するため、メッセージごとに長い遅延が生じてメッセージが完全に失われる場合もあります。 ゲームを現実的な状況に即したものにするため、Photonクライアントライブラリを使用すればインターネット通信の影響をシミュレーションできます:ラグ、ジッター、パケットロスなどです。 - ラグ / レイテンシー: クライアントとサーバー間のメッセージに生じる、ある程度一定した遅延。

どちらの方向も異なる面で影響を受けますが、これらの値は互いに近いものになります。 ラウンドトリップタイムに影響します。 - ジッター: シミュレーションでのラグをランダム化します。 これは、ラウンドトリップタイムの変動に影響します。 この影響でUDPパッケージに異常が生じる可能性があり、この可能性もシミュレーションされます。

新たなラグはLag + [-JitterValue..+JitterValue]です。 これによって、ラグ平均の設定は保持されます。一部のパッケージは、ラグが示す値よりも速い場合があります。 - パケットロス: UPDパッケージが失われる可能性があります。 Photonプロトコルでは高信頼性フラグの付いたコマンドは繰り返されますが、このようにして他のコマンド(オペレーション)は失われる可能性があります。

ラグシミュレーションは、設定で定義された遅延に対応するため独自のスレッドで実行されます。 ほとんどの場合、このスレッドで遅延に対応できますが、実際の遅延には+/-20ミリ秒の変動があります。

目次へ戻る

ネットワークシミュレーションの使用

デフォルトでは、ネットワークシミュレーションはオフになっています。 オンにするにはPhotonPeer.IsSimulationEnabledを設定します。設定はNetworkSimulationSetに集約され、ピアクラスからのアクセスが可能です(たとえば、LoadBalancingClient)。

コードサンプル:

//Activate / Deactivate:
this.peer.IsSimulationEnabled = true;
//Raise Incoming Lag:
this.peer.NetworkSimulationSettings.IncomingLag = 300; //default is 100ms
//add 10% of outgoing loss:
this.peer.NetworkSimulationSettings.OutgoingLossPercentage = 10; //default is 1
//this property counts the actual simulated loss:
this.peer.NetworkSimulationSettings.LostPackagesOut;

目次へ戻る

Photon Server

Photon Serverは、すべてのクライアントが通信に利用する中心ハブです。

Photon ServerはすべてのWindowsマシンで実行可能です。クライアントのUDPおよびTCP接続を処理し、ユーザー独自のビジネスロジック、すなわちアプリケーションとともに.NETランタイムレイヤーをホスティングします。

Photon Server SDKには、複数のアプリケーションがソースや事前に構築された状態で含まれています。 これらのアプリケーションは購入後すぐに実行したり、独自のサーバーロジックの開発に利用できます。 Photon Server SDKはこちらから取得してください。

目次へ戻る

LoadBalancing API

LoadBalancing APIは、Photonでルームベースのゲームを開発するためのサンプルアプリケーションです。また、独自のゲームを開発するうえで柔軟性の高い基盤となります。 LoadBalancing APIはルーム、ルームへの参加と退出、ルーム内の他のプレイヤーへのイベントの送信、プロパティの処理などを実現します。LoadBalancing APIはクライアントライブラリと緊密に連携しており、ほとんどのPhotonドキュメントでサンプルとして使用されます。

目次へ戻る

Photonでのプロパティ

LoadBalancing APIは汎用メカニズムを実装し、サーバー側(メモリ内)でキー/値のセットの設定やフェッチをおこないます。 これらのキー/値のセットはルームやゲーム、またはルーム内のプレイヤーに関連づけられ、ゲーム内に存在するプレイヤーは全員フェッチやアップデートが可能です。 プロパティHashtableのエントリーは個々のプロパティと認識されるため、個別に上書きできます。 プロパティの値には、すべてのシリアライズ可能なデータ型を設定できます。 キーは文字列型またはバイト型のいずれかに設定する必要があります。 より好ましいのは、オーバーヘッドが少ないバイト型です。 混乱を避けるため、キーの型に文字列とバイトを混在させないでください。 キーの型を混在させるとフェッチする際に個別の要求が必要になります。

プロパティのブロードキャストとイベント: ゲーム内のプロパティの変更を「ブロードキャスト」することができ、ブロードキャストは他のプレイヤーがそれらのプロパティをアップデートするイベントのトリガーとなります。 プロパティを変更したプレイヤーは、同じアップデートを取得しません。 ブロードキャストオプションを使用する変更はすべて、プロパティアップデートイベントをトリガーします。

このイベントは変更されたプロパティのみを転送し、プロパティの変更元と、プロパティの所属が示されます。 クライアントは変更を「マージ」する必要があります(プロパティがキャッシュされた場合)。 プロパティは以下のメソッドで設定できます:

  • LoadBalancingClient.OpSetPropertiesOfActorはアクターのプロパティを設定します。
  • LoadBalancingClient.OpSetPropertiesOfRoomはルームのプロパティを設定します。
  • LoadBalancingClient.OpJoinOrCreateRoomはルームが既存でない場合にルームの初期プロパティを設定し、参加アクターのプロパティを設定します。
  • LoadBalancingClient.OpCreateRoomはルームの初期プロパティや、参加アクターのプロパティを設定します。
  • LoadBalancingClient.OpJoinRoomは参加アクターのプロパティを設定します。

ブロードキャストオプションを使用する変更はすべて、プロパティアップデートイベントEventCode.PropertiesChangedをトリガーします。 このイベントは、キーParameterCode.Propertiesの値としてプロパティを転送します。 また、誰がプロパティを変更したかについての情報は、キーParameterCode.ActorNrに保存されています。 キーParameterCode.TargetActorNrは、プロパティセットが特定のプレイヤーに属する場合のみ利用可能です。 このキーが表示されない場合、プロパティはルームプロパティです。

目次へ戻る

 ドキュメントのトップへ戻る