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() をそのまま呼ぶだけで出来るようになりました.
https://t.co/d2ovdt2poo
— 黒河優介(YusukeKurokawa) (@wotakuro) 2021年3月26日
ここでILを直接弄る事によって…
「普通のメソッド呼び出ししているように書いているのに、実はRPC呼び出しが行われる」実現しているのに痺れました
なるほどな… (← わかってない)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 を使ってみましょう.以下のサンプルを利用します.
テキストチャット送信時の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 して動作確認してみます.
プレイ中に 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); } }
これで左クリックを押してみて挙動を確認してみます.
左クリックを押している方のクライアントでは銃の発砲処理は行われていませんが, 別のクライアントではしっかり行われていますね.まあこんな使い方もありますということで.
一応旧 MLAPI の Convenience RPC では InvokeClientRpcOnEveryoneExcept(method, OwnerClientId) を使って 気軽に呼び出せたのですが… まあ今後はこのような形で使っていきましょうって感じかな.
以下,補足.
すてき!補足するとRPCを自分だけ走らせないのはテンポが大事なネトゲだとよくあるやつです!
— izm (@izm) 2021年3月29日
自分のローカル端末では遅延なしで実行したい処理(エフェクトとか)をこの仕組みでやります!
勉強になります. 自クライアントではローカルで処理し,リモートへはRPCで処理をするのがよさみです.