アヒルのある日

株式会社AHIRUの社員ブログです。毎週更新!社員が自由に思いついたことを書きます。

Unity Lobby/Relay入門 (#3)

こんにちは、ちゃらいプログラマです。
Unity Lobby/Relayの最後はRPC送受信周りになります。

※前回の記事はこちら

Unity Lobby/Relay入門 (#1) - アヒルのある日
Unity Lobby/Relay入門 (#2) - アヒルのある日

実装としては、#1で用意していた

public class NetworkObjectBehaviour : NetworkBehaviour
{
}

を使用します。

ただ、このままだとNetworkObjectBehaviourを使用する手段がないので、#2で用意した「MultiGameManagerクラス」に「NetworkObjectBehaviour」のアクセサを定義します。

private NetworkObjectBehaviour m_networkObject = null;
public NetworkObjectBehaviour GetNetworkObject()
{
    // Relayネットワークから切断するとnullになります。
    // エラー処理は別途必要です。
    return m_networkObject ;
}
public void SetNetworkObject(NetworkObjectBehaviour networkObject)
{
    m_networkObject  = networkObject;
}

では、RPC周りを実装していきます。

public class NetworkObjectBehaviour : NetworkBehaviour
{
    // Startで処理しているのはNetworkManager(Unity用意)の生成処理を待つ必要があるためです
    public void Start()
    {
        // NetworkObjectBehaviourはRelayネットワークに接続された人数分生成されます
        // IsOwner()で自分自身かどうかを判定できるので、自身の場合に設定しておきます
        if(IsOwner())
        {
            MultiGameManager.Instance.SetNetworkObject(this);
        }
    }
    
    // RelayネットワークでのRPCルールについて
    // ホスト管理方式を採用しているため、ホスト以外のプレイヤーはホスト経由で送受信する必要があります
    // 受信側の関数に以下の属性を付与します
    // [Unity.Netcode.ClientRpc]
    //    クライアントで実行されるようになります
    //    関数の末尾に【Rpc】を付与しないとエラーになります
    // [Unity.Netcode.ServerRpc( RequireOwnership = false )]
    //    ホストで実行されるようになります
    //    RequireOwnershipはfalseにしていないと自身の所有権のみしか操作できないのでfalseにしておきましょう
    //    関数の末尾に【Rpc】を付与しないとエラーになります
    
    // ケース1:ホストから全員(ホスト含む)に送信する
    // [Unity.Netcode.ClientRpc]属性の関数を用意します
    // ホストもクライアント判定のため同関数が実行されます
    public void SendStart()
    {
        // ホストのみ実行するように判定します
        // IsHostは用意されていますので使用させてもらいます
        if(IsHost)
        {
            ReceiveStartClientRpc();
        }
    }
    [Unity.Netcode.ClientRpc]
    private void ReceiveStartClientRpc()
    {
        // ホスト、クライアント共に実行されます
    }
    
    // ケース2:クライアントからホストに送信する
    // [Unity.Netcode.ServerRpc]属性の関数を用意します
    public void SendClientToHost()
    {
        // クライアントのみで実行できるようにします
        if(!IsHost)
        {
            ReceiveClientToHostServerRpc();
        }
    }
    [Unity.Netcode.ServerRpc( RequireOwnership = false )]
    private void ReceiveClientToHostServerRpc()
    {
        // ホストのみで処理するようにします
        if(IsHost)
        {
            // 処理を記述
        }
    }
    
    // ケース3:自分自身以外に送信する
    // [Unity.Netcode.ClientRpc][Unity.Netcode.ServerRpc]属性の関数を用意します
    public void SendOther()
    {
        // ホストの場合はクライアントへ反映する
        if(IsHost)
        {
            ReceiveOtherClientRpc();
        }
        // クライアントの場合はホスト経由で他クライアントへ反映する
        else
        {
            ReceiveOtherServerRpc();
        }
    }
    [Unity.Netcode.ClientRpc]
    private void ReceiveOtherClientRpc()
    {
        // 自身の情報を反映しないように判定を追加します
        // IsLocalPlayerは用意されているので使用させてもらいます
        if(!IsLocalPlayer)
        {
            // 他クライアントの情報を反映する処理
        }
    }
    [Unity.Netcode.ServerRpc( RequireOwnership = false )]
    private void ReceiveOtherServerRpc()
    {
        // ホストはクライアント関数をそのまま呼び出せばOKです
        ReceiveOtherClientRpc();
    }
}

実際にMultiGameManager経由で実行する場合は

MultiGameManager.Instance.GetNetworkObject().SendStart()

のようになります。

いかがでしたでしょうか。
実際に使用するとなると、【ホスト移譲】を実装する必要がありますがかなりややこしいので機会があれば…
個人的にはPhotonEngineで提供されている

Fusion2 のトポロジー:ホストモード

と同じだと感じました。

ではまた次回!