同期と状態

すべてのプレーヤーを更新し、同じ状態を維持することがゲームの基本です。他のプレーヤーが誰であるか、何をしているか、どこにいるか、ゲームワールドがどのように見えているかを知る必要があります。

PUN(また、Photon一般)は更新および状態維持のためにいくつかのツールを提供しています。このページでは、その選択肢とそれぞれを使用する状況を説明します。

オブジェクトの同期

PUNでは、簡単に特定のゲームオブジェクトを「ネットワーク対応」させることができます。 PhotonViewコンポーネントを割り当てれば、オブジェクトはリモート複製と位置、回転、および他の値を同期することができます。Transformやスクリプト(より一般的)のようなコンポーネントを「監視」するためにはPhotonViewを設定する必要があります。

ほとんどのデモがObject Synchronization(オブジェクトの同期)を使用しています。いくつかのスクリプトはOnPhotonSerializeView()``を実装し、PhotonViewの観察されたコンポーネントとなります。OnPhotonSerializeView()``では、位置およびその他の値は、ストリームに書き込まれそこから読み取られます。

リモートプロシージャコール(RPC)

ルーム内の任意のクライアントからメソッドが呼び出し可能になるようマークすることができます。属性[PunRPC]ChangeColorToRed()を実装すると、リモートプレイヤーは次を呼ぶことでゲームオブジェクトの色を赤に変更できます:photonView.RPC("ChangeColorToRed", PhotonTargets.All);

呼び出しは常にゲームオブジェクト上の特定のPhotonViewを対象とします。 ChangeColorToRed()が呼ばれる場合、そのPhotonViewを持つゲームオブジェクト上でのみ実行されます。特定のオブジェクトに影響を与えたい場合に便利です。

もちん、ターゲットの無いメソッドの場合は空のゲームオブジェクトを「ダミー」としてシーンに入れることができます。たとえば、RPCを使用して、特定のゲームオブジェクトに関連しないチャットを実装することもできます。

RPCは「バッファリング」することができます。サーバーは呼び出しを記憶して、RPCが呼ばれた後に参加したユーザーに送信します。これにより、動作を格納し、PhotonNetwork.Instantiate()の代替を実装することができます。「手動でインスタンス化」を参照してください。

カスタムプロパティ

Photonのカスタムプロパティには、必要に応じて埋めることができる、キー値のハッシュテーブルがあります。値はクライアントで同期およびキャッシュされるので使用する前に取得する必要はありません。変更はSetCustomProperties()で他のプレイヤーにプッシュされます。

これはどのように役立つのでしょうか?通常、ルームやプレイヤーにはGameObjectに関連していない次のような属性があります:現在のマップや、プレイヤーのキャラクターの色(2d jump and runを想像してください)。これらは、オブジェクトの同期またはRPCで送信することができますが、カスタムプロパティを使用する方が便利な場合があります。

プレーヤーのカスタムプロパティを設定するには、PhotonPlayer.SetCustomProperties(Hashtable propsToSet)を使用して、追加または更新を行うためにキーとなる値を追加します。ローカルプレイヤーオブジェクトへのショートカットはPhotonNetwork.playerです。同様に、PhotonNetwork.room.SetCustomProperties(Hashtable propsToSet)を使用して、入室しているルームを更新します。

更新の配布には少し時間がかかりますが、クライアントはそれに応じてroom.customPropertiesplayer.customPropertiesを更新します。プロパティが変更された場合のコールバックとして、PUNはOnPhotonCustomRoomPropertiesChanged(Hashtable propertiesThatChanged)またはOnPhotonPlayerPropertiesChanged(object[] playerAndUpdatedProps)をそれぞれ呼び出します。

新しいルームを作成する際にプロパティを設定することもできます。ルームプロパティをマッチメーキングに使用することもできるので、便利です。参加可能なルームをフィルタリングするためにプロパティ・ハッシュテーブルを使用するJoinRandomRoom()オーバーロードがあります。ルームを作成する際、RoomOptions.customRoomPropertiesForLobbyを設定して、ロビーでフィルタリング可能なルームプロパティを定義してください。

マッチメイキングのドキュメントでは、カスタムプロパティをマッチメイキングに使用する方法を説明します。

プロパティのCheck And Swap(CAS)

SetCustomPropertiesを使用すると、サーバは通常、任意のクライアントから新しい値を受け入れます。これは、少し複雑になる場合があります。

たとえば、プロパティはルーム内の固有アイテムを誰が拾ったかを格納するために使用できます。つまり、プロパティのキーがアイテムで、値がそれを拾った者を定義します。いかなるクライアントも、そのactorNumberにプロパティをいつでも設定することができます。全員がほぼ同時にそれを行うと、最後のSetCustomProperties呼び出しがアイテムを獲得(最終値を設定)します。それは反直感的で、好ましくありません。

SetCustomPropertiesには条件として使用できる任意のexpectedValues​​パラメータがあります。expectedValuesでは、サーバは現在のキーとなる値がexpectedValues​​内のものと一致した場合にのみ、プロパティを更新します。期限の切れたexpectedValuesの更新は無視されます(クライアントは結果としてエラーを取得し、他の者は更新の失敗を知ることがありません)。

この例では、固有のアイテムを受け取る所有者をexpectedValuesに含めることができます。全員がアイテムを取ろうとしても、他のすべての更新要求のexpectedValuesに期限切れの所有者が含まれているので、最初の要求のみが成功します。

SetCustomPropertiesに条件としてexpectedValues​​を使用することをCheck and Swap(CAS)と呼びます。これは、並行性の問題を回避するのに便利ですが、他にもクリエイティブな方法で使用することができます。

注意:SetCustomPropertiesはCASで失敗するかも知れないので、すべてのクライアントはサーバから送信されるイベントのみで、それらのカスタムプロパティを更新します。これは、新しい値を設定しようとするクライアントを含みます。これは、CASを使用せずに値を設定する場合とは異なるタイミングです。

同期、RPC、プロパティを有効に利用

値に対して最適な同期方法を決定するためには、更新を必要とする頻度と、値の 「歴史」が必要かどうかを確認することをお勧めします。

頻繁な更新(位置、キャラクター状態)

頻繁な更新にはObject Synchronizationを使用します。任意の更新数に対して、ストリームに何も書かないことにより、独自のスクリプトで更新をスキップすることができます。

キャラクターの位置は頻繁に変更されます。各更新は重要ですが、すぐに新しいものと交換される可能性が高いです。 PhotonViewを設定して、「Unreliable」または「Unreliable On Change」を送信することができます。前者は、キャラクターが移動しなかった場合でも、固定の周波数で更新を送信します。後者はゲームオブジェクト(キャラクター、単位)停止すると更新の送信を停止します。

頻度の少ない更新(プレイヤーの行動)

キャラクターの装備の使用、道具の使用、ゲームのターンの終了等はすべての頻度の低い動作です。これらはユーザーの入力に基づいていて、RPCとしての送信が最適な場合が多いです。

Object Synchronizationを使用する行はあまり明確ではありません。Object Synchronizationを行う場合は、より頻繁な更新の動作は「インライン」した方が良いかも知れません。例えば:キャラクターの位置を送信する場合は、「ジャンプ」の状態を送信するために簡単に値を追加することができます。これで、独立したRPCを使用する必要はありません!

photonView.RPC("rpcName", ...)を呼び出す際、RPCは瞬時に送信されません。代わりに、Object Synchronizationの周波数が更新を送信するまでバッファリングされます。これは、より少ないパッケージにRPCを集約しますが(オーバーヘッドを防ぐため)、変数のラグを生みます。このローカルラグを防ぐには、PhotonNetwork.SendOutgoingCommands()を呼び出すことで、RPCの更新ループを終えることができます。これは、ゲームがターンの送信などでRPCに頼っている場合などに有効です。

Object Synchronizationとは異なり、RPCはバッファリングされることがあります。バッファされたRPCは、後から参加するプレイヤーに送信されます。これは、動作を次々にプレイする必要がある場合に便利です。例えば、参加するクライアントは、シーン内で誰がどのようにツールを配置して、誰がどのようにそれをアップグレードしたかをリプレイすることができます。後者は、最初の動作に依存します。

新規プレイヤーにバッファされたRPCを送信するにはバンド幅がかかり、クライアントは「ライブ」のゲームプレイに入る前に、各アクションをプレイバックおよび適用する必要があります。これは厄介な場合があり、余分なバッファリングは弱いクライアントを壊してしまう可能性があるので、バッファリングは注意して使用する必要があります。

頻度の少ない更新や状態(ドアの開閉、地図、キャラクター装備)

頻度のとても少ない変更は通常、Custom Propertiesに格納するのが最適です。

バッファリングされたRPCとは異なり、Hashtableプロパティには現在のキーとなる値のみが含まれます。これは、ドアの状態を「オープン」(またはクローズ)にする場合に最適です。プレイヤーはドアが以前にどのように開け閉めされたかを知る必要はありません。

上記のRPCの例では、誰かがシーン内に道具を置いて、それがアップグレードされます。少ない動作にRPCを使用することは問題ありません。修正が多い場合は、プロパティの単一の値で現在の状態を集約する方が簡単です。複数の「防衛+10」の更新は、沢山のRPCではなく、単一の値に簡単に記憶することができます。

Custom PropertyとRPCの使い分け方に明確な決まりはありません。

Custom Propertiesを使用する良いユースケースは、ルームの「開始時刻」を格納することです。ゲームが始まったら、プロパティとしてPhotonNetwork.timeを格納します。この値は、ルーム内のすべてのクライアントで(ほとんど)同じで、開始時間を使用すればクライアントはゲームが実行されている時間の長さや誰のターンかを計算することができます。また、各ターンの開始時間を格納することもできます。これは、ゲームを一時停止することができる場合に最適です。PUNパッケージのInRoomRoundTimerクラスを参照してください。

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