これちのPost-it

技術ネタをペラペラと

Unity の Netcode for GameObjects について調べたメモ

Netcode for GameObjects で自分が気になって調べたことをメモ代わりにまとめておく。

Netcode for GameObjects が何かとか、How to Use 系の記事は他にたくさんあるのでそのあたりは触れません。

参考にしたサイト

About Netcode for GameObjects | Unity Multiplayer Networking

Unity Netcode for GameObjectsを使ったオンラインマルチプレイゲーム開発Tips【Advent Calendar 12/23】|Colorful Palette

ClientRPC

ClientRpc | Unity Multiplayer Networking

  • サーバーは ClientRPC を実行することで(デフォルトで)全クライアントに対して命令を行える

  • Host から ClientRPC を実行する場合は Host 自身もクライアントとして振る舞うため自身の ClientRPC も実行される

    • 同じスタックで呼ばれるため無限ループにならないよう注意
  • 宣言するには [ClientRPC] アトリビュートをつけるのと、メソッド名に ClientRpc プレフィックスをつけないといけない

    • それと前提として NetworkBehavior 継承のクラスにする必要がある
  • 特定のクライアントにのみ send したい場合は ClientRpcSendParameters を使う。以下公式からコピペ。

private void DoSomethingServerSide(int clientId)
{
    // If isn't the Server/Host then we should early return here!
    if (!IsServer) return;


    // NOTE! In case you know a list of ClientId's ahead of time, that does not need change,
    // Then please consider caching this (as a member variable), to avoid Allocating Memory every time you run this function
    ClientRpcParams clientRpcParams = new ClientRpcParams
    {
        Send = new ClientRpcSendParams
        {
            TargetClientIds = new ulong[]{clientId}
        }
    };

    // Let's imagine that you need to compute a Random integer and want to send that to a client
    const int maxValue = 4;
    int randomInteger = Random.Range(0, maxValue);
    DoSomethingClientRpc(randomInteger, clientRpcParams);
}

[ClientRpc]
private void DoSomethingClientRpc(int randomInteger, ClientRpcParams clientRpcParams = default)
{
    if (IsOwner) return;

    // Run your client-side logic here!!
    Debug.LogFormat("GameObject: {0} has received a randomInteger with value: {1}", gameObject.name, randomInteger);
}

ServerRPC

ServerRpc | Unity Multiplayer Networking

  • クライアントからサーバに情報を送信する際実行する RPC

    • クライアントからしか送信できない&サーバーかホストしか受信できない
  • 宣言には [ServerRpc] アトリビュートと ServerRpc プレフィックスをメソッドにつける必要がある

    • NetworkBehavior 継承のクラスである必要がある
  • 自身が NetworkObject のオーナーではない場合は ServerRpc の実行が許可されない

    • しかし [ServerRpc(RequireOwnership = false)] を宣言すれば任意のクライアントが ServerRpc を実行できるっぽい(以下公式をコピペ)
[ServerRpc(RequireOwnership = false)]
public void MyGlobalServerRpc(ServerRpcParams serverRpcParams = default)
{
    var clientId = serverRpcParams.Receive.SenderClientId;
    if (NetworkManager.ConnectedClients.ContainsKey(clientId))
    {
        var client = NetworkManager.ConnectedClients[clientId];
        // Do things for this client
    }
}

public override void OnNetworkSpawn()
{
    MyGlobalServerRpc(); // serverRpcParams will be filled in automatically
}
  • ServerRpcParams.Receive.SenderClientId で Rpc を実行したクライアントの特定が可能
    • 他の引数がある場合は引数の最後に ServerRpcParams を宣言する必要がある
    • 以下の例ではクライアントが銃を打った時にクライアントの PlayerObject から lookWorldPosition に向けて Ray を打つサンプル(以下公式をコピペ)
[ServerRpc(RequireOwnership = false)]
public void PlayerShootGunServerRpc(Vector3 lookWorldPosition, ServerRpcParams serverRpcParams = default)
{
    var clientId = serverRpcParams.Receive.SenderClientId;
    if (NetworkManager.ConnectedClients.ContainsKey(clientId))
    {
        var client = NetworkManager.ConnectedClients[clientId];
        var castRay = new Ray(client.PlayerObject.transform.position, lookWorldPosition);
        RaycastHit rayCastHit;
        if (Physics.Raycast(castRay, out rayCastHit, 100.0f))
        {
            // Handle shooting something
        }
    }
}

なお、RequireOwnership を false にしない場合は上の例で Vector3 引数だけ設定すれば良いらしい。

[ServerRpc]
public void PlayerOwnerShootGunServerRpc(Vector3 lookWorldPosition)
{
    if (NetworkManager.ConnectedClients.ContainsKey(OwnerClientId))
    {
        var client = NetworkManager.ConnectedClients[OwnerClientId];
        var castRay = new Ray(client.PlayerObject.transform.position, lookWorldPosition);
        RaycastHit rayCastHit;
        if (Physics.Raycast(castRay, out rayCastHit, 100.0f))
        {
            // Handle shooting something
        }
    }
}
  • ホストで ServerRpc を実行したら自身の ServerRpc が実行されるので無限ループにならないよう注意が必要

NetworkVariable

NetworkVariable | Unity Multiplayer Networking

  • サーバとクライアント間で RPC など使わずに値の同期ができる仕組み

  • テンプレート <T> の型の値のコンテナのラッパーなので、その値は NetworkVariable.Value で取得する

  • Rpc との使い分け

    • RPC vs NetworkVariable
    • Rpc はその瞬間にのみ有用な一時的なイベントや情報を送受信したい時に使う
      • 後から参加したプレイヤーにそれまで送られた Rpc が自動で送られることは無い
    • NetworkVariable はずっと必要な永続的なステートや情報の同期に使う

最後に

Unity の Relay についても今度調査したい。まだベータ版。ベータ中は無料みたい。