【Unity】Unityのオンライン関係の機能についてざっくりまとめてみる

# ■ はじめに
オンラインゲーム作りたいなーと思ったので、個人的にまとめてみたいと思います。
(UGSは機能が豊富なので、やる気があれば当記事を随時更新します。)



<br>



# ■ UGSについて
- Unity Gaming Services(UGS)：Unityが提供する便利なツール群のこと。
    - ネットワーク関係の機能や、マッチング機能、ボイスチャット機能などのオンライン用のツール群
    - ゲームのパフォーマンス管理、プレイヤーの行動とかを記録する機能もあるっぽい
    - まだベータ版の機能が多い

https://unity.com/ja/solutions/gaming-services

## ● 料金
https://unity.com/solutions/gaming-services/pricing#:~:text=Revenue%20share-,Multiplayer,-Relay


<br>



# ■ 機能一覧
## ● Netcode
- **接続、同期**などの機能を簡単に扱える
- ローカルマルチプレイは、最低これだけでも実装可能

### 使用例
こんな感じに同期できます
![Test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/fa12138c-d0ab-902e-0236-1dd9b47f2cf8.gif)



- ゲームの開始はこんな感じのメソッドを使う
    ```ゲームに参加.cs
    NetworkManager.Singleton.StartServer();      // サーバーとして接続開始
    NetworkManager.Singleton.StartHost();        // ホストとして接続開始
    NetworkManager.Singleton.StartClient();      // クライアントとして接続開始
    ```

<br>

- NetworkObjectコンポーネントをつけると、オブジェクトの生成や破棄などを同期できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/d22fb581-b6cd-e6a2-8329-2601a8eb34b7.png)
- NetworkTransformコンポーネントをつけると、位置の同期ができる

<br>

- [**NetworkBehaviour**](https://docs-multiplayer.unity3d.com/netcode/current/basics/networkbehavior/)クラスを継承したスクリプトをNetworkObjectコンポーネントがアタッチされたオブジェクトにアタッチすると、[**RPC**](https://docs-multiplayer.unity3d.com/netcode/current/advanced-topics/messaging-system/)や[**ネットワーク変数**](https://docs-multiplayer.unity3d.com/netcode/current/basics/networkvariable/)などを利用できる

```RPCとかについて.cs
[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);
}
```



[**🔗参考リンク集**](https://qiita.com/kiku09020/items/2c6c366027ab8d040d66#netcode)


## ● Transport
- **ネットワーク系の処理のSDK**
- UDP, WebSocketを利用している
- マルチプラットフォーム対応可能
- TransportコンポーネントでRelayを指定すると、簡単にオンライン機能を使えるようになる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/ad85183f-f9d0-1546-ed1a-4a6d3965334b.png)


- 手動でも細かく設定してサーバーに接続したりできるぽい

https://docs-multiplayer.unity3d.com/transport/current/about/index.html


## ● Authentication
- プレイヤーの認証系の機能を使える
- **RelayやLobbyなどの機能を使う際に必須**

### 使い道
- オンラインゲームでのプレイヤーの情報の保存など
- マルチプラットフォームでの連携サービスによるデータの同期
- 

### サインイン
- **匿名認証**：他の連携サービスを利用せずに匿名でゲームにサインインする
    - ソシャゲなどの「ゲストでログイン」的な感じ
- **サードパーティー認証**：GooglePlayやApple Game Center、SteamなどのサードパーティーのIDプロバイダを利用してゲームにサインインする

### 使用例
- 小規模なゲームで、マルチプラットフォームを考えてなければ、基本的に匿名認証でいい
    ```匿名認証.cs
    async void Awake()
    {
        // サービス初期化
        await UnityServices.InitializeAsync();
    
        // サインイン時にログ表示
        AuthenticationService.Instance.SignedIn += () => {
            print("Sined in:" + AuthenticationService.Instance.PlayerId);
        };
    
        // サインイン
        await AuthenticationService.Instance.SignInAnonymouslyAsync();
    }
    ```

- サードパーティー認証
    - `Project Settings/Authentication`でIDプロバイダを設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/6eeadbd7-e501-c04a-c7f0-01a6e782bb1d.png)

    - コード：(随時更新予定)



[**🔗参考リンク集**](https://qiita.com/kiku09020/items/2c6c366027ab8d040d66#unity-authenticatoin)



## ● Relay
- Unityが提供する**サーバーサービス**
- Relay Server、Relay Allocationという機能がある
    - Server：サーバー系の操作
    - Allocation：サーバーに対してプレイヤー用の空きスロットを予約する感じの操作をする
- Steamworksなどの別のサーバーサービスを利用する場合、Relayは使わなくていいぽい

### 使用例
```RelayManager.cs
/* Fields */
[SerializeField] int allocationCount = 1;

//-------------------------------------------------------------------
/* Methods */
public async Task<string> CreateRelay()
{
    try {
        // 割り当て作成
        var allocation = await RelayService.Instance.CreateAllocationAsync(allocationCount);

        // サーバーデータ設定
        var relayServerData = new RelayServerData(allocation, "dtls");
        var transport = Unity.Netcode.NetworkManager.Singleton.GetComponent<UnityTransport>();
        transport.SetRelayServerData(relayServerData);

        // 参加コードの取得
        var joinCode = await RelayService.Instance.GetJoinCodeAsync(allocation.AllocationId);

        // ホストとして開始
        Unity.Netcode.NetworkManager.Singleton.StartHost();

        print($"サーバーを作成しました({allocation.AllocationId})");
        return joinCode;
    }
    catch (RelayServiceException e) {
        Debug.LogException(e);
        throw e;
    }
}

public async Task JoinRelay(string joinCode)
{
    try {
        // コードをもとに参加
        var joinedAllocation = await RelayService.Instance.JoinAllocationAsync(joinCode);

        // サーバーデータ設定
        RelayServerData serverData = new RelayServerData(joinedAllocation, "dtls");
        Unity.Netcode.NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(serverData);

        // クライアントとして開始
        Unity.Netcode.NetworkManager.Singleton.StartClient();
        print($"サーバーに参加しました({joinedAllocation.AllocationId})");
    }
    catch (RelayServiceException e) {
        Debug.LogException(e);
        throw e;
    }
}
```

[**🔗参考リンク集**](https://qiita.com/kiku09020/items/2c6c366027ab8d040d66#relay)



## ● Lobby
- ロビー(部屋)を作成して、他のプレイヤーとマッチングする機能
- フレンド間でロビーコードを共有して参加できる、いわゆるプライベートマッチなども再現できる
- ロビーの検索機能も充実していて、フィルター機能や並び替え機能もある



### 使用例
```ロビー作成.cs
/// <summary> ロビー作成 </summary>
public async Task<Lobby> CreateLobby(string relayJoinCode)
{
    string lobbyNameID = Random.Range(1, 10000).ToString("D4");
    string lobbyName = "Lobby" + lobbyNameID;

    // ロビー設定
    CreateLobbyOptions options = new CreateLobbyOptions {
        // Relayの参加コードをロビーのデータに登録する
        Data = new Dictionary<string, DataObject> {
            {LOBBYS_JOIN_CODE_KEY, new DataObject(DataObject.VisibilityOptions.Member, relayJoinCode ,DataObject.IndexOptions.S1) },
        },
    };

    try {
        // まだ削除されていない場合、
        await UniTask.WaitWhile(() => isCreated, cancellationToken: destroyCancellationToken);

        // ロビー作成
        var lobby = await LobbyService.Instance.CreateLobbyAsync(lobbyName, 2, options);

        // イベント追加
        await LobbyService.Instance.SubscribeToLobbyEventsAsync(lobby.Id, EventCallbacks);

        print($"ロビー({lobby.Id})を作成しました");

        // 作成したロビー情報を保存
        isCreated = true;
        JoinedLobbyID = lobby.Id;

        return lobby;
    }
    catch (LobbyServiceException e) {
        Debug.LogException(e);
        throw e;
    }
}
```

```ロビー検索.cs
/// <summary> ロビー検索 </summary>
public async Task<IReadOnlyList<Lobby>> SearchLobbies()
{
    // 検索オプション指定
    QueryLobbiesOptions options = new QueryLobbiesOptions {
        Count = queryCount,        // 検索結果数

        // フィルター指定
        Filters = new List<QueryFilter> {
            new QueryFilter(QueryFilter.FieldOptions.AvailableSlots, "0", QueryFilter.OpOptions.GT),        // 満員のロビーは検索しない
        },

        // ソート順指定
        Order = new List<QueryOrder> {
            new QueryOrder(false, QueryOrder.FieldOptions.AvailableSlots),									// 空き人数の降順で並び変え
        },
    };

    try {
        // 検索
        QueryResponse response = await LobbyService.Instance.QueryLobbiesAsync(options);
        return response.Results;
    }
    catch (LobbyServiceException e) {
        throw e;
    }
}
```

[**🔗参考リンク集**](https://qiita.com/kiku09020/items/2c6c366027ab8d040d66#lobby)


## ● MatchMaker(編集中)
- マッチング関係の機能を扱える
- プレイヤーのランクに応じたマッチングなどを簡単に実装できる



## ● Multiplayer Tools
- プロファイラーや、ネットワークのシミュレーションができる機能などがまとまったツール群

### プロファイラー
- 送信、受信量とかをゲーム画面上で確認できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/ef35fad0-acd3-9f45-e0c1-d3f8bdfe1924.png)


https://docs.unity3d.com/Packages/com.unity.multiplayer.tools@1.0/manual/index.html




## ● Multiplayer Play Mode (MPPM)
- **Editor上で複数のゲームウィンドウやコンソールを表示できる機能**
- プレイヤーに対してタグを付与して、チーム分けしたりできる
- ビルドする手間が減るのですごく便利ですが、プレイヤーごとにプロジェクト用のフォルダが作成されるので、容量取られるのが欠点
- また、**Unityバージョン2023.1以降でのみ利用可能**
- 現状ベータ版ですが、すごく便利


https://docs-multiplayer.unity3d.com/mppm/current/about/

### 使用例
- こんな感じでエディター上で2人分使えるので、ものすごく便利！！
![ConnectScene_.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/dfc6c7d6-8ce8-4a3f-4ddc-f7caedc5141d.gif)
    - コンソールビューやシーンビューもプレイヤーごとに作成されるので、混同しなくてわかりやすい！

<br>

- Unity Authenticationとの併用には注意が必要
    - **普通に匿名サインインすると、同じIDが反映されてしまうので、プレイヤーごとに異なるIDを割り当てる必要がある**
    - 一番簡単なのは、プレイヤーがゲームを起動するたびにIDを変更する方法
        ``` .cs
        protected override async void Awake()
        {
            base.Awake();

            // サービス初期化
            await UnityServices.InitializeAsync();

            AuthenticationService.Instance.SignedIn += () => {
                print($"{AuthenticationService.Instance.PlayerId} was signedin.");
            };

        #if UNITY_EDITOR
            // リセット(MPPM用)
            AuthenticationService.Instance.ClearSessionToken();
        #endif

            await AuthenticationService.Instance.SignInAnonymouslyAsync();

            SetEvents();
        }
        ```

        - ただ、この方法だと**IDが変更されて起動するたびにオンラインで保存されたデータなどがリセットされる**
        - 今後のMPPMのアップデートに期待…？


<br>

# ■ さいごに
- 情報量が多く、学習コスト高めですが、汎用性高めなのでPhotonよりも便利だと思います
- でも結構コストかかるんで、簡易的なプロジェクトはPhotonでいいかもです

## ● 参考
### UGS
https://unity.com/ja/solutions/gaming-services



### Netcode
https://docs-multiplayer.unity3d.com/netcode/current/about/index.html

https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@1.6/api/index.html

参考動画

https://www.youtube.com/watch?v=3yuBOB3VrCk



### Unity Authenticatoin
https://docs.unity.com/ugs/manual/authentication/manual/overview

https://docs.unity.com/ugs/en-us/manual/authentication/manual/approaches-to-authentication

https://docs.unity3d.com/Packages/com.unity.services.authentication@3.0/api/Unity.Services.Authentication.html

### Relay
https://docs.unity.com/ugs/manual/relay/manual/introduction

https://docs.unity.com/ugs/en-us/packages/com.unity.services.relay/1.0

参考動画

https://youtu.be/msPNJ2cxWfw?si=It6Yd_8lQw_Px23X



### Lobby
https://docs.unity.com/ugs/en-us/manual/lobby/manual/unity-lobby-service

https://docs.unity.com/ugs/en-us/packages/com.unity.services.lobby/1.0/api/Unity.Services.Lobbies

参考動画

https://youtu.be/-KDlEBfCBiU?si=EFdLvk6hu7hgOKhb

### Other
Unity TransPort

https://docs-multiplayer.unity3d.com/transport/current/about/index.html

Multiplayer Tools

https://docs.unity3d.com/Packages/com.unity.multiplayer.tools@1.0/manual/index.html

MPPM

https://docs-multiplayer.unity3d.com/mppm/current/about/



<br>