🔖

Unity+MagicOnion4.1.xを試す 環境構築&サービスでの通信編

2021/02/18に公開

チャプター

まえがき

  • リアルタイム通信をするにあたってMagicOnionが良さそうだったので調査と環境構築
  • サンプルで利用しているコードはMagicOnionのREADMEをほぼ利用しています
  • この記事ではServiceのみ取り扱い、StreamingHubに関しては取り扱わない
  • ディレクトリやプロジェクト名は適宜読み替えてください

環境

ディレクトリ構成

  • 相対パスで参照する箇所があるので、クライアント、サーバーすべて同ディレクトリ内に入れる
MyApp
  └┬MyApp-Client
   ├MyApp-Server
   └MyApp-Shared

クライアント(Unity)の環境構築

新しいプロジェクトを作成

Api Compatibility Levelの変更

  • Player Settings > Other Settings > Api Compatibility Level 4.xに変更する

MagicOnionをimport

  • GitHubのReleasesからMagicOnion.Client.Unity.unitypackageをDLしてプロジェクトにimport

MessagePack-CSharpをimport

  • GitHubのReleasesからMessagePack.Unity.2.2.85.unitypackageをDLしてプロジェクトにimport

gRCPをimport

  • gRCPのビルドからgrpc_unity_package.2.37.0-dev202102141315.zipをDL
    • 最新のビルドが必要であればDaily Buildsから最新のTimestampのBuild IDからDL

  • zipを解凍してPluginsの中にあるディレクトリをUnity側にコピー

GitHubなどで管理する場合

  • 赤で囲っているが、Google.Protobufは不要だと思われるので入れなくて良さそう
  • iOS向けのlibgrpc.aのファイルサイズが100MBを余裕で越えているのでREADMEのStripping debug symbols from ios/libgrpc.aを参考にstripしてあげると良い
    • ここでいうstripコマンドはXCodeのstripなのでMac環境じゃないと動かないと思われる
  • Grcp.Core/runtimes配下で不要なOSのディレクトリは削除して問題ないと思われるので利用しないOSがあれば削除推奨

必要なディレクトリを作成

  • Assets > Scripts > MyApp > Shared > Service

サーバーと接続するためのインタフェースを作成

Assets/Scripts/MyApp/Shared/Services/IMyFirstService.cs
using MagicOnion;

namespace MyApp.Shared.Services
{
    // Defines .NET interface as a Server/Client IDL.
    // The interface is shared between server and client.
    public interface IMyFirstService : IService<IMyFirstService>
    {
        // The return type must be `UnaryResult<T>`.
        UnaryResult<int> SumAsync(int x, int y);
    }
}

コントローラーを作成

Assets/Scripts/MyApp/MyFirstController.cs
using Grpc.Core;
using MagicOnion.Client;
using MyApp.Shared.Services;
using UnityEngine;

namespace MyApp
{
    public class MyFirstController : MonoBehaviour
    {
        private Channel _channel;
        private IMyFirstService _service;

        void Awake()
        {
            _channel = new Channel("localhost", 5000, ChannelCredentials.Insecure);
            _service = MagicOnionClient.Create<IMyFirstService>(_channel);
        }

        async void Start()
        {
            var x = Random.Range(0, 1000);
            var y = Random.Range(0, 1000);
            var result = await _service.SumAsync(x, y);
            Debug.Log($"Result: {result}");
        }

        async void OnDestroy()
        {
            if (_channel != null)
            {
                await _channel.ShutdownAsync();
            }
        }
    }
}

Sceneにスクリプトを追加

  • 適当なScene(今回はお試しなのでデフォで存在しているSampleScene)に空のゲームオブジェクトを追加してMyFirstController.csをAdd Compornentする

ここまでで一旦、クライアント(Unity)側の作業は終了

サーバー側の環境構築

プロジェクトの作成

  • VisualStudioで新しいプロジェクトをgRPCサービスを作成
  • .NET 5.0を利用するように変更

不要なファイルの削除

  • Protos、ServicesはMagicOnionを利用する上で不要なので削除

MagicOnion.Serverを追加

  • NuGetでMagicOnion.ServerMyApp-Serverに対して追加

Startup.csの修正

Startup.cs
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;

 namespace MyApp
 {
     public class Startup
     {
         public void ConfigureServices(IServiceCollection services)
         {
             services.AddGrpc();
+            services.AddMagicOnion(); // Add this line
         }
         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
             if (env.IsDevelopment())
             {
                 app.UseDeveloperExceptionPage();
             }
             app.UseRouting();
             app.UseEndpoints(endpoints =>
             {
                 // Replace to this line instead of MapGrpcService<GreeterService>()
+                endpoints.MapMagicOnionService();
-                endpoints.MapGrpcService<GreeterService>();
                 endpoints.MapGet("/", async context =>
                 {
                     await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
                 });
             });
         }
     }
 }

共有プロジェクトの作成

  • ソリューションエクスプローラーから新しいプロジェクトを追加

    • MyApp-Shared
  • クラスライブラリ(.NET Standard)で作成

MagicOnion.Abstractionsを追加

  • NuGetでMagicOnion.AbstractionsMyApp-Sharedに対して追加

MessagePack.UnityShimsを追加

  • NuGetでMessagePack.UnityShimsMyApp-Sharedに対して追加

クライアント(Unity)のShared配下を参照

  • MyApp-Shared.csprojを開いてクライアントで作成したShared配下を参照する

MyApp-Shared.csproj
 <Project Sdk="Microsoft.NET.Sdk">

   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
     <RootNamespace>MyApp.Shared</RootNamespace>
   </PropertyGroup>

   <ItemGroup>
     <PackageReference Include="MagicOnion.Abstractions" Version="4.1.2" />
     <PackageReference Include="MessagePack.UnityShims" Version="2.2.85" />
   </ItemGroup>

+  <ItemGroup>
+    <Compile Include="..\MyApp-Client\Assets\Scripts\MyApp\Shared\**\*.cs" />
+  </ItemGroup>
</Project>

MyApp-ServerにMyApp-Sharedの参照を追加

  • ソリューションエクスプローラーからプロジェクを参照でMyApp-Sharedを選択

Serviceの作成

services/MyFirstService.cs
using System;
using MagicOnion;
using MagicOnion.Server;
using MyApp.Shared.Services;

namespace MyApp.Services
{
    // Implements RPC service in the server project.
    // The implementation class must inehrit `ServiceBase<IMyFirstService>` and `IMyFirstService`
    public class MyFirstService : ServiceBase<IMyFirstService>, IMyFirstService
    {
        // `UnaryResult<T>` allows the method to be treated as `async` method.
        public async UnaryResult<int> SumAsync(int x, int y)
        {
            Console.WriteLine($"Received:{x}, {y}");
            return x + y;
        }
    }
}

接続確認

サーバーの起動

  • F5などでサーバーを起動
  • 下記のようなログが出れば起動OK
"C:\Program Files\dotnet\dotnet.exe" D:/MagicOnion/MyApp/MyApp-Server/bin/Debug/net5.0/MyApp-Server.dll
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\MagicOnion\MyApp\MyApp-Server

クライアント(Unity)のシーンを実行

  • SampleSceneを実行してコンソールにログが出ればOK

  • サーバー側にもログが出力されているはず
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'gRPC - /IMyFirstService/SumAsync'
Received:750, 621
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'gRPC - /IMyFirstService/SumAsync'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/2 POST http://localhost:5000/IMyFirstService/SumAsync application/grpc - - 200 - application/grpc 78.6627ms

参考

GitHubで編集を提案

Discussion