Unity Gaming ServersのLobby/Relayの使い方

# 概要
前回 Unity Netcode を利用してThirdPersonサンプルをローカルネットワーク同期することに成功しました

https://qiita.com/tsune2ne/items/faeb0b274b30c4ae7196

今回はUnity Gaming ServicesのLobby / Relayを使って
NAT超えでのネットワーク同期を行います

## Unity Gaming Servicesとは
https://unity.com/ja/solutions/gaming-services

ゲーム開発に必要な機能がパッケージ化され提供されています
今回は以下の3点を使います
・マルチプレイヤーフレームワークであるNetcode
・ゲームロビーを作成できるLobby
・P2Pネットワークでプレイヤー同士を繋ぐRelay

Multiplayerの使用には支払い情報が必要です
少人数開発中は支払いが発生するケースは少ないですが
大人数になると発生する可能性があるので注意です

https://unity.com/solutions/gaming-services/pricing?_ga=2.199788398.1569230259.1667052329-825398908.1663114648

下記までは無料です
> Lobby : 10 GiB / month per regional group: US + EU and Asia + Australia
> Relay : First 50 CCU for free / month

## NAT超えとは
通信に必要なIPアドレスには世界中から自宅や会社を特定するグローバルIPアドレスと
自宅や会社内で端末ごとに振られるプライベートIPアドレスがあります

自宅や会社のルーター内では端末からグローバルネットに通信する際、プライベートIPアドレスをグローバルIPアドレスに変換しています（Network Address Translation、通称NAT）
これは内から外に通信する分には便利なのですが、外から内に通信するにはプライベートIPアドレスがわからず通信できません

オンラインゲームではグローバルネットから自宅・会社内の端末に通信する必要があるため
NAT超えの方法が重要となります



# 環境
Unity : 2022.1.14f1
Lobby : 1.0.3
Relay : 1.0.4
Netcode for GameObjects : 1.0.2

# フロー
### Project作成
まずUnity Gaming Services上でプロジェクトを作ります
UnityID > プロジェクト管理 > SignIn > Unity Gaming Services
Projects > Create Projects

プロジェクト名と対象年齢を入れてCreate Project

### Unity Gaming ServicesとProjectの設定
プロジェクトの設定でLobbyとRelayを利用可能にします

UnityID > プロジェクト管理 > SignIn > Unity Gaming Services
Multiplayer > 画面上部から対象プロジェクトを選択
Get Started > Continue setup > 支払い情報を登録 > Complete

「Set up Multiplay」が表示されますが、今回はGame Server Hostingを使わないのでスルーします

左メニューのRelay > Setup Relayを押すとRelayのセットアップが始まります
同じ要領でLobbyもセットアップしておきます


### Lobby/Relay用意フローと該当コード
1. Lobbyのオーナーフロー
    1. Auth認証
        1. ```UnityServices.InitializeAsync();```
        1. ```AuthenticationService.Instance.SignInAnonymouslyAsync();```
    1. Relay鯖にAllocation ※1
        1. ```RelayService.Instance.CreateAllocationAsync(maxConnections);```
    1. Relay鯖へのJoinCode取得
        1. ```RelayService.Instance.GetJoinCodeAsync(hostAllocationId);```
    1. Lobby作成
        1.  ```Lobbies.Instance.CreateLobbyAsync(lobbyName, maxPlayers, createOptions);```
    1. allocationからリレー鯖情報を取得してNetworkManagerに設定
        1. ```NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(allocation.RelayServer.IpV4, (ushort)allocation.RelayServer.Port, allocation.AllocationIdBytes, allocation.Key, allocation.ConnectionData);```
    1. Hostとしてゲーム開始 ※2
        1. ```NetworkManager.Singleton.StartHost();```
    1. 一定時間ごとにLobbyにHeartBeat ※3
        1. ```Lobbies.Instance.SendHeartbeatPingAsync(lobbyId);```
1. Lobbyのゲストフロー
    1. Auth認証
        1. ```UnityServices.InitializeAsync();```
        1. ```AuthenticationService.Instance.SignInAnonymouslyAsync();```
    1. Lobbyリスト取得
        1. ```Lobbies.Instance.QueryLobbiesAsync();```
    1. Lobby参加
        1. ```Lobbies.Instance.JoinLobbyByIdAsync(lobbyId, joinOptions);```
    1. JoinCodeでRelay鯖に参加
        1. ```RelayService.Instance.JoinAllocationAsync(joinCode);```
    1. allocationから鯖情報を取得してNetworkManagerに設定 ※4
        1. ```NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(allocation.RelayServer.IpV4, (ushort)allocation.RelayServer.Port, allocation.AllocationIdBytes, allocation.Key, allocation.ConnectionData, allocation.HostConnectionData);```
    1. Clientとしてゲームに参加
        1. ```NetworkManager.Singleton.StartClient();```

※1 LobbyサンプルではLobby鯖を先に用意してからRelay鯖を用意してますが、JoinCodeを登録するのが手間だったのでRelayを先に行いました
※2 Dedicatedサーバーでも要点は同じはず
※3 HeartBeatを定期的に打たないとLobbyが自動終了します
※4 NetworkManagerへの登録はLobbyサンプルから変わってるので注意。多分あってるはず...

# 感想
かなり簡単にロビー機能とNAT超えを実装することができました

Lobbyの検索フィルタや入室コード、ランダム入室にも対応しており便利そうです



# 参考資料
https://github.com/Unity-Technologies/com.unity.services.samples

- Lobby/Relayの最小規模サンプル
- Netcodeへのつなぎこみはやってくれないので注意

https://docs-multiplayer.unity3d.com/netcode/current/learn/bossroom/bossroom/index.html

- Netcodeサンプルの一つ
- Relay周りが最新版と少し違う