RpcParams を利用してRPCを受けるクライアントの選択を行う【MLAPI】

新 MLAPI では RPC が新生されています.

検証環境

  • Unity 2020.3.1f1
  • MLAPI v0.1.0

New Standard RPC

旧 MLAPI では RpcMethod を作って,呼び出す時に InvokeClientRpcXXXXX(RpcMethod method) のような感じでした.
こちらは Photon 使ってたら馴染みがある感じだと思います.

新 MLAPI では InvokeClientRpcXXXX って書く必要がなくて,RpcMethod() をそのまま呼ぶだけで出来るようになりました.

なるほどな… (← わかってない)Mono.Cecil 使っているのでだいたいそういう感じなんだというのはわかる

Rpc メソッド名の制約

[ClientRpc]アトリビュートを加えた場合は末尾に ClientRpc を,[ServerRpc] の場合は末尾に ServerRpc を付ける必要があります.
逆にアトリビュートを付けてないメソッドに XXXXXXRpc を加えるとエラーが生じるので注意.

RpcParams

New Standard RPC ではパラメタの最後に ServerRpcParams / ClientRpcParams をオプションとして加えることが出来ます.

ServerRpcParams

struct ServerRpcSendParams
{
}

struct ServerRpcReceiveParams
{
    // RPC の送信者
    ulong SenderClientId;
}

struct ServerRpcParams
{
    ServerRpcSendParams Send;
    ServerRpcReceiveParams Receive;
}

// デフォルトの呼び出し方
[ServerRpc]
void AbcdServerRpc(int somenumber) { /* ... */ }

// オプション
[ServerRpc]
void XyzwServerRpc(int somenumber, ServerRpcParams serverRpcParams = default) { /* ... */ }

ClientRpcParams

struct ClientRpcSendParams
{
    // RPC の受信者
    ulong[] TargetClientIds;
}

struct ClientRpcReceiveParams
{
}

struct ClientRpcParams
{
    ClientRpcSendParams Send;
    ClientRpcReceiveParams Receive;
}

// デフォルトの呼び出し方
[ClientRpc]
void AbcdClientRpc(int framekey) { /* ... */ }

// オプション
[ClientRpc]
void XyzwClientRpc(int framekey, ClientRpcParams clientRpcParams = default) { /* ... */ }

Quick Start サンプルを改変して試してみる

簡単ですが,この RpcParams を使ってみましょう.以下のサンプルを利用します.

xrdnk.hateblo.jp

テキストチャット送信時のRPCを以下のようにします.

        /// <summary>
        /// テキストチャットを送信する
        /// </summary>
        /// <param name="serverRpcParams">ServerRpcParams</param>
        [ServerRpc(RequireOwnership = true)]
        public void SubmitMessageServerRpc(ServerRpcParams serverRpcParams = default)
        {
            if (_sceneScript != null)
            {
                _sceneScript.SetMessage($"SenderId:{serverRpcParams.Receive.SenderClientId}[{_networkPlayerName.Value} says hello {Random.Range(10, 99)}]");
            }
        }

これで Send Message して動作確認してみます.

gyazo.com

プレイ中に NetworkObject を見ると諸々情報が見られます.
serverRpcParams.Receive.SenderClientId には Send Message を押した Client の OwnerId が飛ばされていますね.

次に奇妙ですが,銃の発射処理は走らせる時は自分以外を指定するようにしてみましょう.
発射処理実行時のRPCを以下のようにします.

        /// <summary>
        /// サーバーへ発射処理を走らせる
        /// </summary>
        /// <param name="serverRpcParams">ServerRpcParams</param>
        [ServerRpc(RequireOwnership = true)]
        private void FireWeaponServerRpc(ServerRpcParams serverRpcParams = default)
            => FireWeaponClientRpc(
                new ClientRpcParams { Send = new ClientRpcSendParams
                    { TargetClientIds = 
                        NetworkManager.Singleton.ConnectedClientsList
                        .Where(c => c.ClientId != OwnerClientId)
                        .Select(c => c.ClientId).ToArray() }
                });

        /// <summary>
        /// 全クライアントへ発射処理を送る
        /// </summary>
        /// <param name="clientRpcParams">ClientRpcParams</param>
        [ClientRpc]
        private void FireWeaponClientRpc(ClientRpcParams clientRpcParams = default)
        {
            var bullet = Instantiate(_activeWeapon.weaponBullet, _activeWeapon.weaponFirePosition.position,
                _activeWeapon.weaponFirePosition.rotation);
            bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * _activeWeapon.weaponSpeed;
            if (bullet)
            {
                Destroy(bullet, _activeWeapon.weaponLife);
            }
        }

これで左クリックを押してみて挙動を確認してみます.

gyazo.com

左クリックを押している方のクライアントでは銃の発砲処理は行われていませんが,
別のクライアントではしっかり行われていますね.まあこんな使い方もありますということで.

一応旧 MLAPI の Convenience RPC では InvokeClientRpcOnEveryoneExcept(method, OwnerClientId) を使って
気軽に呼び出せたのですが… まあ今後はこのような形で使っていきましょうって感じかな.

以下,補足.

勉強になります.
自クライアントではローカルで処理し,リモートへはRPCで処理をするのがよさみです.

参考資料

docs-multiplayer.unity3d.com