RPC

オブジェクトのプロパティ、状態、位置の同期は、正確で自然なマルチプレイヤー体験を保証する上ために重要です。しかし、プレイヤーが実行するアクションも同期する必要があります。

プロパティの値をポーリングして、その変化から特定のアクションを検出することも不可能ではありませんが、ほとんどの場合、アクションを他のプレイヤーに中継して適切な関数を呼び出す方が良い結果を生みます。

リモートプロシージャコール(RPC)は、プレイヤー間で送信されるメッセージで、関数のシグネチャとオプションとして引数を含んでいます。これを使って、他のプレイヤーのワールドにあるオブジェクトのメソッドを直接呼び出すことができます。

Strixは、RPC関連の一連の機能でこの仕組みを実現します。

登録

注釈

RPCの登録は、オーナーとレプリカの両方のGameObjectで行う必要があります。

メソッドをRPCに変換するには、それにStrixRpc属性の印を付けるのみです。

[StrixRpc]
void MyRPCMethod()
{
    // ...
}

StrixBehaviourから派生したクラスのメソッドのみがRPCになることができます。メソッドにStrixRpc属性を付けるとプロシージャコードというものがメソッドに割り当たります。これは呼び出しの際にメソッドを識別するために使用します。属性のProcedureCodeプロパティを使用して、このコードを直接指定できます。指定しなければ、メソッド名のハッシュが使用されます。

RPCの送信

Strixは、RPCを送信するための複数の方法を提供します。これらは、RPCを送信する先のメンバーが異なります。特定のメンバーに向けて送信したRPCは、そのクライアントのインスタンス上で、そのRPCが登録されているオブジェクトについて呼び出されます。

RPCを呼び出すときに指定できるターゲットにはいくつかのタイプがあります。

  1. 特定のルームメンバーでRPCを呼び出す

void Rpc(UID to, string rpcName, params object[] args)
void Rpc(UID to, string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args)
  1. このメンバーでRPCを呼び出す

void Rpc(string rpcName, params object[] args)
void Rpc(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args)
  1. ルームの現在のオーナーでのみRPCを呼び出します

void RpcToRoomOwner(string rpcName, params object[] args)
void RpcToRoomOwner(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args)
  1. 現在のメンバーを除く全てのメンバーでRPCを呼び出す

void RpcToOtherMembers(string rpcName, params object[] args)
void RpcToOtherMembers(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args)
  1. 自分を含む全てのルームメンバーでRPCを呼び出す

void RpcToAll(string rpcName, params object[] args)
void RpcToAll(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args)

呼び出すRPCの名前は、nameofで取得できます。

nameof(MyRPCMethod);

RPC引数

全てのRPC送信関数は、引数の配列を取ることができます。

注釈

RPCを送信するとき、Strixはこの引数の配列が特定の関数シグネチャに対して有効であるかどうかを確認しません。正しい引数のシグネチャを使用するように十分に注意してください。そうしないと、RPCの受信が失敗する可能性があります。

argsのための配列は、特定の関数の引数のリストを順番に表します。関数が1つの引数しか取らない場合でも、引数を配列に入れなければなりません。

Strixは、これらの引数として、一部の限定された型のみをシリアル化できます。

シリアル化可能な型

プリミティブ型

  • bool

  • char

  • byte

  • sbyte

  • short

  • ushort

  • int

  • uint

  • long

  • ulong

  • float

  • double

  • byte[]

  • string

  • DateTime

  • 列挙型

コンテナー

  • 配列

  • List<>

  • LinkedList<>

  • HashSet<>

  • Dictionary<,>

  • ICollectionまたはICollection<>を継承する全てのコレクション

  • IDictionaryまたはIDictionary<,>を継承する全てのコレクション

注釈

全ての要素(ディクショナリの場合にはキーも)も、ここに列挙したシリアル化可能な型でなければなりません。

Unityの型

  • Vector2

  • Vector3

  • Vector4

  • Vector2Int

  • Vector3Int

  • Quaternion

  • Matrix4x4

  • Color

  • Color32

  • Rect

  • RectInt

  • Bounds

  • LayerMask

カスタムクラス

また、カスタムクラスをシリアル化することもできます。その場合はObjectFactory.Instance.Register()を使用して登録します。カスタムクラスの全てのフィールドとプロパティの型もシリアル化可能である必要があります。つまり、上記の型のいずれかであるか、事前に手動で登録しておく必要があります。

RPCの例

コード内に、キャラクターに与えられたダメージを記述する構造体があるとします。

struct DamageInfo
{
    public int Points;
    public Vector3 Direction;
}

これをRPCメソッドのパラメーターとして使用する場合は、使用する前に登録する必要があります。これは一度のみ行うことができます。ゲーム開始処理のどこかで行うといいでしょう。

public class SomeGameManager : MonoBehaviour
{
    public void Start()
    {
        ObjectFactory.Instance.Register(typeof(DamageInfo));
    }
}

この型の引数を使ってRPCを呼び出す前に、送信側クライアントと受信側クライアントの両方で、この行を実行しておくようにします。

その後、次のように使用できます。

public class Character : StrixBehaviour
{
    [StrixRpc]
    public void MakeDamage(DamageInfo damageInfo)
    {

    }
}
gameObject.GetComponent<Character>().Rpc(
    nameof(Character.MakeDamage),
    new DamageInfo {
        Points = 10,
        Direction = (transform.position - gameObject.transform.position).normalized
    }
);

RpcContext

受け取ったRPCに関するメタ情報を取得することもできます。そのためには、メソッドの最後の引数としてStrixRpcContextを追加する必要があります。メソッドのシグネチャのみが変更され、呼び出しは同じままですので、注意してください。RPCを送信するときには、コンテキスト自体を除く全ての引数を提供します。コンテキストは、RPCが呼び出されたときにSDKによって自動的に提供されます。ここから、RPCを送信したルームメンバーとクライアントに関する情報を抽出できます。

[StrixRpc]
public void MakeDamage(DamageInfo damageInfo, StrixRpcContext strixRpcContext)
{
    Debug.Log("Received " + damageInfo.Points + " damage from " + strixRpcContext.sender.GetName());
}

警告ログ

RPCの使用中に、さまざまな警告メッセージが表示される場合があります。これらのメッセージの意味、メッセージの原因、修正方法を以下に示します。

  • Can't use RPC when not joined to room (ルームに参加していないときはRPCを使用できません)

    考えられる原因: まだルームに参加していないか、ルームに入っていたが接続が解除されました。

    修正方法: ルームに参加するか再参加してからRPCをもう一度使用してみます。また、ルームを出た後、ロジックが正しく機能していることを確認します。

  • Can't use RPC for %target% because it does not have StrixReplicator attached (StrixReplicatorがアタッチされていないため、%target%にRPCを使用できません)

    考えられる原因: RPC関数を呼び出しているオブジェクトにStrixレプリケーターコンポーネントがありません。

    修正方法: RPCを呼び出すオブジェクトにStrixレプリケーターがあることを確認します。

  • Can't use RPC for %target% because networkInstanceId is not assigned yet. (networkInstanceIdがまだ割り当てられていないため、%target%にRPCを使用できません。)

    考えられる原因: RPC関数を呼び出しているオブジェクトでStrixレプリケーターが同期を行っていません。

    修正方法: RPCを実行する前に、Strixレプリケーターがアタッチされていて、同期していることを確認します。

  • StrixReplicator not found for instanceId: %instanceId% (次のインスタンスIDのStrixReplicatorが見つかりません: %instanceId%)

    考えられる原因: レプリケーターが予期せず削除されました。

    修正方法: レプリケーターが削除されていないことを確認します。

  • RPC method not found for object type: %objectType% (次のオブジェクトタイプのRPCメソッドが見つかりません: %objectType%)

    考えられる原因: RPCがこのクライアントに登録されていない可能性があります。さらに、RPCの送信先のオブジェクトが正しいものではない場合があります。

    修正方法: 全てのクライアントが送信または受信するRPCを登録するようにします。呼び出すオブジェクトが同期されていることを確認します。

  • RPC method not found: %rpcHash% (RPCメソッドが見つかりません: %rpcHash%)

    考えられる原因: RPCがこのクライアントに登録されていない可能性があります。

    修正方法: 全てのクライアントが送信または受信するRPCを登録するようにします。

  • RPC Component not found for type %Type% (タイプ%Type%のRPCコンポーネントが見つかりません)

    考えられる原因: メソッドを呼び出す特定のスクリプトコンポーネントが削除された可能性があります。

    修正方法: RPC呼び出しを行う前にスクリプトが削除されないようにします。

  • Failed to invoke Rpc: %name%. %message% (RPCの呼び出しに失敗しました: %name%。%message%)

    考えられる原因: 引数の逆シリアル化の際かRPCの呼び出し中にエラーが発生しました。

    修正方法: 引数のシリアル化と逆シリアル化が正しく行えることを確認します。呼び出し先が有効であることを確認します。