ねののお庭。

かりかりもふもふ。

UnityでgRPC使う時の一連の流れとか。

はい、素でgRPCのデイリービルドからUnity用のzip落としてきて、Unityに突っ込んでも動きません。 やっかいな。。。

Unity側でgRPC使えるようにするための一連の流れを説明して、動作を確かめるべくサーバ側(ASP.NET Core)も用意します。

Unity側

Unity用のパッケージに含まれているGoogle.Protobufが原因で怒られていて、これはnugetから適切なのを落としてあげればいいわけです。 怒られている原因はGoogle.Protobuf.dllがSystem.Memory参照してるけどバージョンあってない、とかそういう問題ぽい。

なのでnugetからv10.1以前のパッケージを落としてきて、それを使います。 v11以降はnet45でもSystem.Memoryに依存してるけど、それ以前は依存してないからね。(System.Memoryを使ってないということは当然Span以前の物になるので、性能は落ちそう。しらんけど。)

.nupkgは.zipなので、.zipに拡張子を替えて解凍してlib/net45からdllを引っこ抜いてUnityのGoogle.Protobuf.dllと置き換えましょう。 これでエラーは消えて、ハッピー。

次に.protoファイルからRPC用のC#ファイルを作ります。 普通の.NETでcsprojがかける環境だと、

<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

とか

<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

こんな具合で書いておけば、勝手にC#ファイル吐き出しといてくれるんだけど、Unityだとそうもいかない。 なのでせこせことprotocコマンドを叩きます。

protocコマンド、chocoとかでも落とせのですが、RPCに関するものは生成できないみたいなので、nuget toolからGrpc Toolsを引っ張ってきます。 ちなみにnuget cliもchocoから落とせる。

適当なディレクトリで

nuget install Grpc.Tools

とかすると、Grpc.Tools.2.xx.xみたいなディレクトリができて、protocとかが一通り入ったものが落とせます。

ここではProtosってディレクトリにgreet.protoファイルが入っているとします。 greet.protoはよくあるサンプルのこれ。

syntax = "proto3";

option csharp_namespace = "GrpcGreeter";

package Greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

で、C#スクリプトを生成するためにこんな感じのコマンドを叩くことになります。

Grpc.Tools.2.30.0\tools\windows_x64\protoc.exe --csharp_out protoc_output --grpc_out protoc_output --plugin=protoc-gen-grpc=Grpc.Tools.2.30.0\tools\windows_x64\grpc_csharp_plugin.exe Protos\greet.proto

長いけどしょーがない。 コマンドの結果としてprotoc_outputというディレクトリにGreet.csGreetGrpc.csが吐き出されている。それぞれ、--csharp_out,--grpc_outの結果。 --grpc_outを使うためには、--pluginの指定が必要、という感じ。 そして最後の引数に所望の.protoファイルを指定。

そして生成された2つのファイルをUnityにつっこんで、適当なスクリプト作ってStartメソッドに以下みたいのを書きます。

async void Start()
{
    Channel channel = new Channel("localhost:5000", ChannelCredentials.Insecure);

    var client = new Greeter.GreeterClient(channel);

    String user = "めろんぱん。";

    var reply = await client.SayHelloAsync(new HelloRequest { Name = user });
    Debug.Log("Greeting: " + reply.Message);
}

これでUnity側はOK。

とはいえ試せないとOKなんだかわからないので、サーバ側もせっせと実装していきます。

サーバ側(ASP.NET Core gRPC)

次にサーバ側ですが、ここではASP.NET Coreつかって見ましょう。 サンプルはGitHubに乗せてます

github.com

ASP.NET CoreでgRPC使うの非常に簡単で、

dotnet new grpc

でテンプレを作って、.csprojに以下のようにprotoを参照。

<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

こういう形で参照してあげれば、protocコマンドから開放されて嬉しい。 生成物としてGreeter.GreeterBaseみたいな継承して使うためのクラスが出来上がるので、継承してあげて中身を実装。

基本的にはこれでdotnet runとかで起動してあげればいいんだけど、ASP.NET CoreのgRPC基本的にセキュア(TLS)じゃないとアクセスさせてくれないので、そのまま起動しただけだとUnity側でチャンネルを作る時以下みたいに、

Channel channel = new Channel("localhost:5000", ChannelCredentials.Insecure);

ChannelCredentials.Insecure使ってた場合アクセスできないので、TLS通して無くてもアクセスできるようにするためにこんな感じにProgram.csの一部を書き換えます。

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }


    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.ConfigureKestrel(options =>
                {
                    // Setup a HTTP/2 endpoint without TLS.
                    options.ListenLocalhost(5000, o => o.Protocols =
                        HttpProtocols.Http2);
                });
                webBuilder.UseStartup<Startup>();
            });
}

これで準備は終わりです。

dotnet run

で起動してあげて、Unityからアクセスしてみてください。

Greeting: Hello めろんぱん。

とかログにでるはず。

以上でとりあえず、UnityでgRPCが使えるようになったわけですが、サーバサイドをC#で書いていいなら、MagicOnionの方が良いような気がしますが、どうでしょう。主にUnityの事考えられているのと、サーバ->クライアントのRPCが非常に書き心地がよいので。gRPCのServer Streaming、うーん。

私はしばらくサーバをPythonで書かなあかんので、せこせことprotocコマンド叩いて行きますよ()