UnityMagicOnion導入方法(2023年最新)

# はじめに
MagicOnionを導入する敷居が高いと聞いていて、実際導入しようとしたら一発で導入できなかったので記事にまとめようと思いました。
出回っている情報でも古くなってしまっているので、2023年の最新バージョンということで書いていきます。

# 環境
## クライアント
Unity 2021.4.14f1
gRPC UnityPackage 2.47.0
MessagePack v2.4.59
MagicOnion v5.0.2

## サーバー
.Net 7.0
MagicOnion v5.0.2

## その他
Rider 2022.3.1

# Unityインストール
## Unityの設定
ProjectSettings/Player/Configuration
Api Compatibility Levelを.NET Frameworkに設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188476/805a2981-4871-e58d-eaa8-5d11905bcc5f.png)

Scriptiong Backendは今回Monoにしていますが、IL2CPPにする場合、[追加の手順](https://github.com/Cysharp/MagicOnion?tab=readme-ov-file#magiconion-code-generator-for-il2cpp)が必要になります。

## gRPC
以下のgrpcのデイリービルドからUnityPackageをダウンロードするのですが、

https://packages.grpc.io/

最近のデイリービルドからUnityPackageがなくなってしまっています。
地道に遡ってみたところ、2022/4/19のビルドには存在していたので、こちらからダウンロードできます。

https://packages.grpc.io/archive/2022/04/67538122780f8a081c774b66884289335c290cbe-f15a2c1c-582b-4c51-acf2-ab6d711d2c59/index.xml

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188476/47eb4063-7e2c-13e4-30a7-f5fb74aa14f6.png)

**---2023/08/05追記---**

Cysharp様がサポートされなくなったgRPCのUnityパッケージの代わりとなるものをリリースしました！
(現時点ではプレビュー版)
gRPCが使えるだけではなくhttp2通信も可能です。

https://github.com/Cysharp/YetAnotherHttpHandler

まだ試していないですが、聞いたところMagicOnionに簡単に導入できるらしいです。

**---2023/12/17追記---**

導入手順を別記事でまとめました！
Unityインストール手順とUnity側のコードを上記の記事で置き換えてご参照ください。

https://qiita.com/KyoheiOkawa/items/2d54551b59e22e2536bf

**---追記終わり---**

ダウンロードして解凍後、UnityのAssets/Plugin配下に
- Google.Protobuf
- Grpc.Core
- Grpc.Core.Api

の３つを配置します。

:::note warn
gRPCで100MBを超えるファイルがあるので、Gitを利用する場合はLFSの設定が必要です。
Assets/Plugins/Grpc.Core/runtimes/android/arm64-v8a/libgrpc_csharp_ext.so
Assets/Plugins/Grpc.Core/runtimes/ios/libgrpc.a
:::

## MessagePack
リリースページから最新版のUnityPackageをダウンロードします。
(2023/1/18現在の最新はv2.4.59)

https://github.com/neuecc/MessagePack-CSharp/releases

ダウンロード完了しましたら、インポートしてMessagePackの導入は完了です。

## MagicOnion
MagicOnionもMessagePackと同様に最新版をリリースページからダウンロードします。
(2023/1/18現在の最新はv5.0.2)

https://github.com/Cysharp/MagicOnion/releases

ダウンロード完了しましたら、インポートして導入完了です。

# サーバーインストール
私はRider上で構築したので、Riderの方法で書いていきます。

## プロジェクト作成
Unityプロジェクトと同じリポジトリ内に.NET CoreのConsole Applicationのプロジェクトを作成します。

![2023-01-18 164251.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188476/84de8f0a-6c2b-e71b-c521-554e0d93a3dd.png)

## MagicOnion
NuGetで現時点での最新バージョン5.0.2をインストールします。

![2023-01-18 164805.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188476/d2623a68-b2e7-04cb-e6d5-2fe0699418e8.png)

MagicOnionをインストールすれば、gRPCやMessagePackなどの必要なパッケージも自動的にインストールされます。
しかし、Unityとは異なるバージョンがインストールされるので、手動でそろえておいたほうが良いかもしれません。

## 起動スクリプト

```Program.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;

static class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.WebHost.UseKestrel(options =>
        {
            options.ConfigureEndpointDefaults(endpointOptions =>
            {
                endpointOptions.Protocols = HttpProtocols.Http2;
            });    
        });
            
        builder.Services.AddGrpc();
        builder.Services.AddMagicOnion();

        var app = builder.Build();

        app.MapMagicOnionService();
        
        app.Run();
    }
}
```

前半のエンドポイントのプロトコルをHTTP2に設定している個所ですが、こちらを設定しないとUnity側がリクエストを送ったときにエラーになってしまいます。

# HelloWorld
シンプルな足し算APIを作成していきます。

## 共通コード
サーバーとクライアントで共通のコードはどこで管理するかは、討論になる話題かと思いますが今回はUnity側に置きます。
Assets配下であれば、任意の場所で大丈夫です。
ex) Assets/Scripts/Shared/

今回使用するインターフェースはこちらです。

```IMyFirstService.cs
using MagicOnion;

namespace App.Reversi.Shared
{
    public interface IMyFirstService : IService<IMyFirstService>
    {
        UnaryResult<int> SumAsync(int x, int y);
    }
}
```

xとyの和を返すシンプルなインターフェスです。

### サーバーサイドのプロジェクトに読み込み
上で作成したインターフェースをサーバーのプロジェクトにも読み込ませる必要があります。
csprojのItemGroupに、csprojからの相対パスでUnity内の共通コードのディレクトリを指定します。

```diff
<ItemGroup>
    <PackageReference Include="MagicOnion.Server" Version="5.0.2" />
+   <Compile Include="..\..\Assets\Scripts\Shared\**\*.cs" LinkBase="LinkFromUnity" />
</ItemGroup>
```

ちなみに私の環境のリポジトリの構造は以下のようになっています。
```
Repository
- Assets
 - Scripts
  - Shared
   - IMyFirstService.cs 
- Packages
- ProjectSettings
- Server ←サーバープロジェクト
 - Server
  - Server.csproj
```

## サーバー側の実装

以下のファイルをプロジェクトに追加するだけで実装完了です。

```MyFirstService.cs
using App.Reversi.Shared;
using MagicOnion;
using MagicOnion.Server;

public class MyFirstService : ServiceBase<IMyFirstService>, IMyFirstService
{
    public async UnaryResult<int> SumAsync(int x, int y)
    {
        Console.WriteLine($"Called SumAsync - x:{x} y:{y}");
        return x + y;
    }
}
```

他のファイルいじる必要がないので、簡単ですね。
おそらく起動スクリプト内でリフレクションで取得しているのかと思います。

そして追加が終わりましたら、プログラムを実行しておきましょう。

## Unity側の実装

以下のファイルを作成して、

```Sample.cs
using App.Reversi.Shared;
using Grpc.Core;
using MagicOnion.Client;
using UnityEngine;

public class Sample : MonoBehaviour
{
    async void Start()
    {
        var channel = new Channel("localhost", 5000, ChannelCredentials.Insecure);
        var client = MagicOnionClient.Create<IMyFirstService>(channel);
        var result = await client.SumAsync(1, 2);
        Debug.Log($"Result: {result}");
        await channel.ShutdownAsync();
    }
}
```

適当なシーンを作成して、適当なゲームオブジェクトにAddComponentして、実行します。

**実行結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188476/0bb99e07-b985-248c-ba67-83010d7fbede.png)

# 最後に
導入に手こずりましたが、導入さえできれば実装は簡単そうですね。
次は、リアルタイム通信の実装方法やその応用について書いていこうと考えています。