RPCs and RaiseEvent

Contents

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

PUNとPUN2に特徴的な、他のPhotonパッケージにはない機能といえば Remote Procedure Calls (RPC)への対応です。

リモートプロシージャコールは名前の通り、 (同じルームにいる)リモートクライアントに対してのメソッドの呼び出しです。

メソッドにリモートの呼び出しを有効にするには[PunRPC]属性を適用する必要があります。

[PunRPC]
void ChatMessage(string a, string b)
{
    Debug.Log(string.Format("ChatMessage {0} {1}", a, b));
}

PunRPCとマークされた関数を呼び出すには、PhotonViewコンポーネントが必要です。 呼び出しの例は以下の通りです。

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

上級者向けのヒント:スクリプトがMonoBehaviourPunである場合、this.photonView.RPC()を使用できます。

ターゲットメソッドを直接呼び出すのではなく、PhotonViewコンポーネントでRPC()を呼び出し、呼び出すメソッドの名称を提供してください。

Back To Top

メソッドシグネチャ

PhotonViewはRPCのための「ターゲット」のようなものです。 つまり、全クライアントは特定のPhotonViewをもつネットワーク化したGameObjectにのみメソッドを実行します。 特定のオブジェクトに当たって"ApplyDamage" RPCを呼び出すと、受信するクライアントはダメージを同じオブジェクトに適用します!

複数のパラメータ(PUNでシリアル化可能である限り)を追加できます。(詳細はこちら "Serialization in Photon その場合、メソッドと呼び出しには同じパラメーターが必要です。 受信クライアントが一致するメソッドを見つけられない場合、エラーを記録します。

この規則には1つの例外があります。 RPCメソッドの最後のパラメーターは、 PhotonMessageInfo型にすることができます。これにより、各呼び出しのコンテキストが提供されます。 呼び出しで PhotonMessageInfoを設定しません。

[PunRPC]
void ChatMessage(string a, string b, PhotonMessageInfo info)
{
    // photonView.RPC()呼び出しは、infoパラメーターなしの場合と同じです。
    // info.Senderは、RPCを呼び出したプレーヤーです。
    Debug.LogFormat("Info: {0} {1} {2}", info.Sender, info.photonView, info.SentServerTime);
}

送信者がPhotonMessageInfo内にいて、PhotonViewを介して「ターゲッティング」する場合、余分なパラメータを追加することなく誰かを撃つ実装ができます。 誰が撃って、何に当たったのか把握できます。

Back To Top

考慮事項

  • 設計上,RPCメソッドを持つスクリプトは,親でも子でもなくPhotonViewとまったく同じGameObjectに,接続する必要があります。
  • RPCメソッドを静的にすることはできません。
  • void以外の戻り値を持つRPCメソッドを呼び出すことはできますが、戻り値は使用されません。 同じメソッドに対する他の明示的な直接呼び出しで戻り値が必要でない限り、常に戻り値として voidを使用してください。
  • RPCメソッドがオーバーライドされる場合、 PunRPC属性を追加することを忘れないでください。 そうでない場合、基本クラスで PunRPC属性を持つオーバーライドされていない継承されたメソッドを使用できます。
  • オーバーロードRPCメソッドを作成しないでください:特別な PhotonMessageInfoの最後のパラメーターを持つものと持たないもの。
  • RPCメソッドごとに一意の名前を使用します。
  • オーバーロードRPCメソッドを作成しないでください:特別な最後のパラメーター PhotonMessageInfoを持つものと持たないもの。 いずれかのオプションを選択します。
  • オブジェクト配列をRPCメソッドのパラメーターとして送信する場合は,最初にオブジェクト型にキャストする必要があります。

    :

        object[] objectArray = GetDataToSend();
    photonView.RPC("RpcWithObjectArray", target, objectArray as object);
    
    
    // ...
    
    
    [PunRPC]
    void RpcWithObjectArray(object[] objectArray)
    {
        // ...
    }

    ### ターゲット、バッファリング、順番

    RPCを実行するクライアントを指定できます。 そのためには、RpcTargetの値を使用します。 もっとも一般的なのはAllのクライアントがRPCを呼ぶようにすることです。 Othersのみの場合もあります。

    RpcTargetには Bufferedで終わる値があります。 サーバーはこれらのRPCを記憶し、新しいプレイヤーの参加時には、そのプレイヤーは参加以前に起こっていたでことであってもRPCを取得します。 この使用に際しては注意を払ってください。バッファリングするリストが長くなると、参加までの時間も長くなってしまいます。

    RpcTarget にはViaServerで終わる値もあります。 送信するクライアントがRPCを実行しなければならない場合、通常はサーバーを通したRPC送信なしで即座に実行されます。 ただしこれは、イベントの順番に影響を及ぼします。ローカルでメソッドを呼び出した場合ラグが発生しないからです。

    ViaServerは「All」ショートカットを無効にします。 RPCが順番におこなわれるべき場合などには特に興味深い点です。 サーバーを通して送信されたRPCは、全ての受信するクライアントで同じ順番に実行されます。 これはサーバーに到着した順番ということになります。

    : あるレースゲームで、「finished」というRPCをAllViaServerとして送信することができます。 始めの「finished」RPC呼び出しが勝者を通知します。 次の「finished」呼び出しで、ランキングにいるメンバーを確認できます。

    ### RPC名のショートカット

    ネットワーク間での送信をするのに文字列は適したものではないので、PUNでは文字列を短くする裏技を使っています。 PUNはエディタ内のRPCを検出してリストをコンパイルします。 それぞれのメソッドの名前は、そのリストを経由してIDを取得し、RPCを名前で呼び出すと、PUNはただIDだけを送信します。

    このショートカットにより、ゲームの異なるビルドはRPCに同じIDを使用しないことがあります。 これが問題になる場合は、ショートカットを無効にしてください。 ただし、同じビルドのクライアントが一致する場合は、問題にはなりません。

    RPCのリストはPhotonServerSettingsを介して保管され、管理されます。

    RPC呼び出しがプロジェクトの異なるビルドでうまくいかなければ、このリストを確認してください。 Get HashCode ボタンがハッシュコードを計算します。これはプロジェクトフォルダ間の比較をするのに便利です。

必要であれば、リストをクリアして(Clear RPCs ボタンをクリック)、Refresh RPC List ボタンをクリックして手動で更新をかけることもできます。

Back To Top

RPCのタイミングと負荷レベル

RPCは、特定のPhotonViewで呼び出され、受信クライアント上で一致するものを常にターゲットとします。 リモートクライアントが一致するPhotonViewをまだ読み込み又は作成していない場合には、RPCは失われます!

このため、クライアントが新しいシーンを読み込むことが、RPC欠落の典型的な原因となります。 この状況は、1つのクライアントが新しいGameObjectを含むシーンをすでに読み込んでいて、他のクライアントがこのGameObjectを理解できない場合(同じシーンを読み込むまで)に発生します。

この状況はPUNで対処できます。 接続する前にPhotonNetwork.AutomaticallySyncScene = trueを設定して、ルームのマスタークライアントでPhotonNetwork.LoadLevel()を使用します。 こうして、ルーム/ゲーム内ですべてのクライアントが読み込む必要があるレベルを、1つのクライアントが定義します。

RPCの欠落を防ぐため、クライアントは受信メッセージの実行を停止できます(これが、LoadLevelが実行する内容です)。 RPCにシーンをロードさせる際には、コンテンツが初期化されるまで、ただちにIsMessageQueueRunning = falseを設定してください。 メッセージキューを無効化すると、キューアンロックするまでメッセージの受信と送信が遅延されます。 このため、続行の準備ができたらキューをアンロックすることは非常に重要です

:

private IEnumerator MoveToGameScene()
{
    // Temporary disable processing of futher network messages
    PhotonNetwork.IsMessageQueueRunning = false;
    LoadNewScene(newSceneName); // custom method to load the new scene by name
    while(newSceneDidNotFinishLoading)
    {
        yield return null;
    }
    PhotonNetwork.IsMessageQueueRunning = true;
}

Back To Top

RaiseEvent

RPCが必要にならないケースもあります。 そのようなケースでは、PhotonViewとメソッドをいくつか呼び出すことが要求されます。

PhotonNetwork.RaiseEventを使用すると、自分のイベントを作成して、ネットワーク化オブジェクトに関係なく送信することができます。

イベントは一意の識別子であるイベントコードを使って表されます。 Photonではこのイベントコードはバイト値で記載され、最大256個のイベントまで許容されます。 ただし、それらの中にはPhoton自身が使用しているものもあるので、カスタムイベントに使えるものは全てではありません。 備わっているイベントを全て除いても、[0~199]の200個のカスタムイベントコードの使用が可能です。 高度なイベントキャッシュ操作を使用する場合は、イベントコード0を避ける必要があります(イベントコードに基づいてキャッシュされたイベントを削除する場合、フィルター内のワイルドカードです)。 以下の例では多くの単位に対して特定の位置に移動するよう伝えるイベントが記述されています。

byte evCode = 1; // Custom Event 1: Used as "MoveUnitsToTargetPosition" event
object[] content = new object[] { new Vector3(10.0f, 2.0f, 5.0f), 1, 2, 5, 10 }; // Array contains the target position and the IDs of the selected units
RaiseEventOptions raiseEventOptions = new RaiseEventOptions { Receivers = ReceiverGroup.All }; // You would have to set the Receivers to All in order to receive this event on the local client as well
SendOptions sendOptions = new SendOptions { Reliability = true };
PhotonNetwork.RaiseEvent(evCode, content, raiseEventOptions, sendOptions);

何をしようとしているのか、よく見てみましょう。 このケースでは、イベントを発生させる前にイベントコードを直接定義しています。 カスタムイベントが複数ある場合、以下のように既に使用したクラス内で定義することをお勧めします。

private readonly byte MoveUnitsToTargetPositionEvent = 1;

RaiseEventメソッドを呼び出すときにこれを使用することもできます:

PhotonNetwork.RaiseEvent(MoveUnitsToTargetPositionEvent, content, raiseEventOptions, sendOptions);

コンテンツは、PUNがシリアライズ化できるものなら何でも大丈夫です。 この例では様々な型が含まれているため、オブジェクトの配列を使用しています。

3番目のパラメータはRaiseEventOptionsを記述しています。 これらのオプションを使用することで、例えばこのイベントがサーバー上でキャッシュされるべきかどうか、このイベントをどのクライアントに受信させるか、また、このイベントをどのインタレストグループに転送させるかを選べるようになります。 これらのオプションを自分で定義するかわりに、デフォルトのRaiseEventOptionsを適用するnullの使用もできます。 送信者にもイベントを受信するようにしたいので受信者をReceiverGroup.Allに設定します。 RaiseEventOptionsについては、このページの下部で詳しく説明しています。

最後のパラメータはSendOptionsを記述しています。 このオプションを使用すると、このイベントがreliableかunreliableか、またメッセージを暗号化するべきかなどを選択できます。 今回の例ではイベントがsent-reliableかどうかを確認します。

Back To Top

IOnEventCallbackコールバック

カスタムイベントの受信には、2つの選択肢があります。 1つ目はIOnEventCallbackインターフェースを実装することです。 実装の際にはOnEventコールバックハンドラーを追加する必要があります。 このハンドラーの関数は以下のような見た目です。

public void OnEvent(EventData photonEvent)
{
    // 何かを行う
}

このハンドラーを適切に登録するために、UnityのOnEnableとOnDisable機能を使用できます。 そうすることで、コールバックハンドラーを適切に追加・削除でき、このハンドラーに関する問題が起こらなくなります。

public void OnEnable()
{
    PhotonNetwork.AddCallbackTarget(this);
}

public void OnDisable()
{
    PhotonNetwork.RemoveCallbackTarget(this);
}

受信した情報に関して、 OnEvent関数は以下のようになります。

public void OnEvent(EventData photonEvent)
{
    byte eventCode = photonEvent.Code;

    if (eventCode == MoveUnitsToTargetPositionEvent)
    {
        object[] data = (object[])photonEvent.CustomData;

        Vector3 targetPosition = (Vector3)data[0];

        for (int index = 1; index < data.Length; ++index)
        {
            int unitId = (int)data[index];

            UnitList[unitId].TargetPosition = targetPosition;
        }
    }
}

まず始めに、受信したイベントコードが設定しておいたコードを一致しているかを確認します。 一致していればイベントのコンテンツを既に送信済のフォーマット、つまりこの例ではオブジェクトの配列にキャストします。 そのあとでその配列から、コンテンツに追加していた最初のオブジェクトである、ターゲットの位置を取得します。 この配列にはバイト値のみが残されている点を把握しているため、forループを使用して残りのデータを処理できます。 この識別子を使用してUnitList(基本的にはリストやディクショナリなどのユニットを含む構造体)から特定の単位を取得し、新たなターゲット位置を適用します。

Back To Top

LoadBalancingClient.EventReceived

カスタムイベントを受信する2つ目のオプションは、イベントが受信されたら呼び出されるメソッドを登録することです。 適切にこれを行うには、1つ目のオプションと同じくUnityのOnEnableとOnDisable機能を使用します。

public void OnEnable()
{
    PhotonNetwork.NetworkingClient.EventReceived += OnEvent;
}

public void OnDisable()
{
    PhotonNetwork.NetworkingClient.EventReceived -= OnEvent;
}

注意:OnEvent関数そのものは IOnEventCallbackセクションの時に説明したことと同じなのでここでは割愛します。

内部的に、PUNはほぼ全てのコミュニケーションに対してRaiseEventも使用します。

Back To Top

Raise Eventオプション

RaiseEventOptionsパラメータを使用すると、どのクライアントがイベントを取得するか、バッファリングされるかなどについて定義できます。

受信者グループ

「受信者グループ」はイベントを誰が受信するのか定義する1つの方法です。 このオプションはRaiseEventOptionsを介して使用可能です。

定義されたグループが以下の通り3つあります。

  • Others:同じルームに参加した,その他全てのアクティブなアクター
  • All:送信者を含む,同じ部屋に参加している全てのアクティブなアクター
  • MasterClient: ルーム内で現在指定されているマスタークライアント

Back To Top

インタレストグループ

「インタレストグループ」とは、誰がイベントを受信するかを定義委するもう一つの方法です。 グローバルグループ「0」を使用して全てのクライアントにイベントを発生させられます。 0ではない特定のグループを使って、指定するグループにイベントを発生させることもできます。 特定のグループに送信されたイベントを受信するには、クライアントは前もってこのグループにサブスクライブしていなければなりません。

インタレストグループについての詳細はこちらでご確認ください。

Back To Top

ターゲットアクター

「ターゲットアクター」はイベントの受信者を定義する3つ目の方法です。 この方法を使用すると、1人もしくはそれ以上のクライアントを、固有のアクターナンバーで選択して、彼らに対してイベントを発生させられます。

Back To Top

キャッシングオプション

最も興味深いオプションは、おそらくイベントキャッシュオプションです。 PUNは、次の2つの場所でイベントキャッシュを使用します。

  • すべての PhotonNetwork.Instantiate *呼び出しはキャッシュされます。
  • RpcTarget。* Bufferedで送信されたすべてのRPCはキャッシュされます。 この場合、PUNは同じ意味で「バッファリングされた」表現を使用します。

イベントキャッシュの詳細[こちら](/ja-jp/pun/v2/gameplay/ cached-events)をご覧ください。

Back To Top

送信オプション

SendOptionsパラメータを使うと、イベントがリライアブルで送信されるか、暗号化されて送信されるかを定義できます。

信頼性

Reliabilityオプションはイベントが送信されるときにリライアブルかアンリライアブルかを表します。

Back To Top

暗号化

Encryptオプションを使うと、イベントを送信前に暗号化するかどうか定義できます。 デフォルトでは、イベントは暗号化されません。ヒント:暗号化は使用の前に、構築しなければなりません。

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