Help us understand the problem. What is going on with this article?

Unity+.NET Core+MagicOnionの環境構築ハンズオン

更新履歴(2019/10/19)

MagicOnion v2.6.3 が公開されました。
それに合わせて gRPC のパッケージへのリンクの修正や、スクリーンショットの更新を行いました。

動作確認は下記の環境で行っています。
- Windows 10
- Unity 2019.1.14f1
- Visual Studio 2019 v16.3.5
- MagicOnion 2.6.3

はじめに

MagicOnion は一度環境を構築してしまえば、触り心地が良くてとても使いやすいフレームワークだと思います。

しかし初期構築がやや複雑で、特に Unity と サーバー(.NET Core) の両方の経験が無い方が挑戦した場合はどこかでつまづいてしまうことも多いのではないかと思いました。

そこで、どちらかの経験が無い方でもつまずかずにポチポチと環境構築できる資料を目指して書いたのがこの記事になります。

もし途中でつまづくところがありましたら Twitter で教えていただけると喜びます。

ハンズオンの所要時間は Unity と VisualStudio をインストール済みの状態から開始して、30分~1時間程度です。

目次

  • 1. 環境や事前準備について
  • 2. Unity 側の構築
  • 3. サーバー側の構築
  • 4. 動作確認
  • 5. IL2CPP対応(コードジェネレーターによるコード生成)
  • 6. マルチスタートアッププロジェクトについて
  • 7. 後書きと参考にさせていただいた記事などへのリンク

1. 環境や事前準備について

1.1. OS について

MagicOnion は Windows と Mac のどちらの環境でも基本的には動作します。
ただし Mac は IL2CPP に対応するためのコードジェネレーターの不具合があるため注意が必要です。

参考Issue
https://github.com/neuecc/MessagePack-CSharp/issues/355

1.2. Unity のインストール

2018.3 以降の Unity をインストールしてください。

1.3. Visual Studio のインストール

UnityHub からインストールすると Visual Studio 2017 がインストールされますが、最新版の 2019 を推奨します。

Visual Studio 2019 は下記のサイトからダウンロードできます。
https://visualstudio.microsoft.com/ja/?rr=https%3A%2F%2Fwww.google.com%2F

エディションは無料で利用できる Community を選びます。
image.png

インストール時に下記のオプションを有効にしてください。

  • Unity によるゲーム開発
  • .NET Core クロスプラットフォームの開発
  • .NET Core 2.2 開発ツール image.png

既に Visual Studio をインストール済みで、の各種オプションがインストールされているか不明な場合は Visual Studio Installer を起動して現状の確認や、追加のインストールが可能です。
image.png

Mac の方は Visual Studio For Mac を利用してください。
こちらもバージョンは 2019 をおすすめします。

1.4. .NET Core 2.2 SDK のインストール

こちらのページからダウンロードが可能です。
Visual Studio が 2019 の場合は Download .NET Core SDK のリンクからダウンロードしてください。
Visual Studio が 2017 の場合は Compatible with Visual Studio 2017 と書かれたリンクからダウンロードしてください。
image.png
※VS2019 なら上の赤枠、VS2017 なら下の赤枠のものをダウンロードしてください。

2. Unity 側の構築

2.1. プロジェクトの新規作成、PlayerSettings の変更、各種フォルダの準備

今回は 3D プロジェクトを作成します。
プロジェクト名は任意ですが、サーバーサイドのプロジェクトと見分けをつけやすくするために Sample.Unity としてみました。

保存先は任意の保存先を入力してください。
image.png

Unity が起動したら PlayerSettings を開きます。
image.png

Player を選択して、下記の2箇所を変更します。

  • APICompatibilityLevel を .NET4.x に変更する
  • Allow unsafe Code にチェックをいれる image.png

次に下記のフォルダを作成します。

  • Sample.Unity\Assets\Editor
  • Sample.Unity\Assets\Plugins
  • Sample.Unity\Assets\Scripts
  • Sample.Unity\Assets\Scripts\Generated
  • Sample.Unity\Assets\Scripts\ServerShared
  • Sample.Unity\Assets\Scripts\ServerShared\Hubs
  • Sample.Unity\Assets\Scripts\ServerShared\MessagePackObjects
  • Sample.Unity\Assets\Scripts\ServerShared\Services
  • GeneratorTools

※Sample.Unity は自分のプロジェクト名に読み替えてください。

image.png

image.png

2.2. MagicOnion のインストール

こちらのページから下記の2つをダウンロードします。

  • MagicOnion.Client.Unity.unitypackage
  • moc.zip

Version はダウンロード可能な最新のものを選択します。
image.png

ダウンロードが終わったら MagicOnion.Client.Unity.unitypackage をダブルクリックしてインポートします。
image.png

次に moc.zip の中身の MagicOnion.UniversalCodeGenerator を GeneratorTools フォルダにコピーします。
image.png

2.3. MessagePack for C# のインストール

こちらのページから下記の2つをダウンロードします。

  • MessagePack.Unity.1.7.3.5.unitypackage
  • MessagePackUniversalCodeGenerator.zip

バージョンは UnityPackage は 1.7.3.5、CodeGenerator は 1.7.3.6 です。

image.png

image.png

ダウンロードが終わったら MessagePack.Unity.1.7.3.5.unitypackage をダブルクリックしてインポートします。

image.png

次に MessagePackUniversalCodeGenerator.zip の中身の MessagePackUniversalCodeGenerator を GeneratorTools フォルダにコピーします。

image.png

2.4. Unity 用の gRPC パッケージの入手

gRPC の Unity 用のパッケージを以下のサイトからダウンロードします。
https://packages.grpc.io/

画面右側の BuildID をクリックすると、そのビルドのパッケージをダウンロードできます。

現在は 2.24.0 を使えばサーバー側と同じバージョンになりますので、下記のリンクからダウンロードします。
https://packages.grpc.io/archive/2019/09/f3cd878e0b6d11cd134be27d0ead7352a527f6de-9c60f3e0-20ab-4d7b-8441-930e1d5588cb/index.xml

ダウンロードの際は grpc_unity_package.2.24.0-dev.zip を選択します。
image.png

ダウンロードが終わったら、Asset\Plugins 以下に中身をコピーします。
image.png

2.5. サーバーへ接続するための Script の用意

Assets\Scenes 以下に SampleController スクリプトを作成します。
image.png

SampleController のコードは以下をコピペしてください。
Start() でサーバーへ接続し、OnDestroy() で切断するようになっています。

※その他の処理は動作確認の章で書き加えます。

using Grpc.Core;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SampleController : MonoBehaviour
{
    private Channel channel;

    void Start()
    {
        this.channel = new Channel("localhost:12345", ChannelCredentials.Insecure);
    }

    async void OnDestroy()
    {
        await this.channel.ShutdownAsync();
    }
}

SampleScene へ空の GameObject を追加します。
image.png

追加した GameObject に SampleController を AddComponent します。
image.png

2.6. Unity ⇔ サーバー間のコード共有の動作確認用の class の用意

MagicOnion を利用する際は Unity 側で作成した class などをサーバーサイドと共有して使うことが一般的です。
今回は Assets\Scripts\ServerShared 以下に作成した script をすべて共有する設定を行います。
この設定作業自体は後でサーバー側の設定で行いますが、先に動作確認用の class を用意しておきます。

Assets\ServerShared\MessagePackObjects 以下に Player スクリプトを作成します。
image.png

Player のコードは以下をコピペしてください。

using MessagePack;
using UnityEngine;

namespace Sample.Shared.MessagePackObjects
{
    [MessagePackObject]
    public class Player
    {
        [Key(0)]
        public string Name { get; set; }
        [Key(1)]
        public Vector3 Position { get; set; }
        [Key(2)]
        public Quaternion Rotation { get; set; }
    }
}

MagicOnion を使用する場合、Client ⇔ サーバー間の通信で使用する class はこのような MessagePackObject として定義します。

MessagePackObject として定義するために必要なことは下記の2つだけですので覚えておきましょう。

  • class に MessagePackObjectAttribute を付与する
  • 各プロパティに KeyAttribute を付与して番号を順番にふる

Unity 側の構築はここまでです。

3. サーバー側の構築

続いてサーバー側の構築作業を進めます。

3.1. ソリューションへサーバー側のプロジェクトを追加

ソリューションを右クリックして、新しいプロジェクトを追加します。
image.png

コンソールアプリ(.NET Core)を選択し、次へをクリックします。
image.png

任意のプロジェクト名を入力します。(ここでは Sample.Server としました)
場所はプロジェクトのルートを指定してください。
image.png

これによってルート直下のフォルダ構成はこのようになります。
image.png

3.2. MagicOnion のインストール

NuGet から MagicOnion をインストールします。
ツール -> NuGet パッケージマネージャー -> ソリューションの NuGet パッケージの管理 を開きます。
image.png

参照 をクリックし、 MagicOnion を検索します。
検索結果から MagicOnion.Hosting を選択し、Sample.Server にチェックをいれてインストールをクリックします。
image.png

ライセンスに同意します。
image.png

3.3. MessagePack.UnityShims のインストール

Unity の Vector3 などをサーバー側で扱えるようにするために、MessagePack.UnityShims をインストールします。
先ほどの MagicOnion のインストールと同じ要領で、NuGet から MessagePack.UnityShims を検索してインストールします。
image.png

3.4. Unity側のコードを読み込む設定

Sample.Server をダブルクリックして、Sample.Server.csproj を開きます。
image.png

Sample.Server.crproj へ下記の設定を追加します。
※Sample.Unity の部分は自分の Unity 側のプロジェクト名に読み替えてください。

  <ItemGroup>
    <Compile Include="..\Sample.Unity\Assets\Scripts\ServerShared\**\*.cs" LinkBase="LinkFromUnity" />
  </ItemGroup>

ファイル全体はこのようになります。
image.png

この状態でソリューションエクスプローラーを見てみると、Unity 側で作った ServerShared フォルダ以下のファイルなどが、サーバー側のプロジェクトと共有できていることがわかります。
image.png

3.5. Program.cs の編集

Program.cs を下記の内容で上書き保存します。
これでサーバー側のプロジェクトを起動すると MagicOnion が起動するようになります。

using Grpc.Core;
using MagicOnion.Hosting;
using MagicOnion.Server;
using Microsoft.Extensions.Hosting;
using System.Threading.Tasks;

namespace Sample.Server
{
    class Program
    {
        static async Task Main(string[] args)
        {
            GrpcEnvironment.SetLogger(new Grpc.Core.Logging.ConsoleLogger());

            await MagicOnionHost.CreateDefaultBuilder()
                .UseMagicOnion(
                    new MagicOnionOptions(isReturnExceptionStackTraceInErrorDetail: true),
                    new ServerPort("0.0.0.0", 12345, ServerCredentials.Insecure))
                .RunConsoleAsync();
        }
    }
}

※ServerPort として指定している IP アドレスは 0.0.0.0 のままで大丈夫です。
※TCP の 12345 ポートが利用できない環境の場合は 80 などの一般的なポートを使用してください。

4. 動作確認

ここからは動作確認を行います。
MagicOnion は普通の API 通信とリアルタイム通信の2種類の通信が利用できますので、それぞれをテストしてみます。

4.1. 普通の API通信

まずは普通の API 通信から試してみます。

4.1.1. Unity側で API の定義を作る

Assets\Scripts\ServerShared\Services 以下に SampleService スクリプトを作ります。
image.png

SampleService の中身は以下をコピペして保存してください。
今回は足し算をしてくれる API と掛け算をしてくれる API を定義してみます。

using MagicOnion;

namespace Sample.Shared.Services
{
    public interface ISampleService : IService<ISampleService>
    {
        UnaryResult<int> SumAsync(int x, int y);
        UnaryResult<int> ProductAsync(int x, int y);
    }
}

4.1.2. サーバー側で API を実装する

Sample.Server を右クリックして、新しいフォルダを追加します。
名前は Services とします。
image.png
image.png

次に Services フォルダ内にクラスを追加します。
image.png

名前は SampleService.cs とします。
image.png

SampleService.cs の中身は以下をコピペして保存してください。

using MagicOnion;
using MagicOnion.Server;
using Sample.Shared.Services;

namespace Sample.Server.Services
{
    public class SampleService : ServiceBase<ISampleService>, ISampleService
    {
        public async UnaryResult<int> SumAsync(int x, int y)
        {
            Logger.Debug($"SumAsync Received:{x}, {y}");
            return x + y;
        }

        public async UnaryResult<int> ProductAsync(int x, int y)
        {
            Logger.Debug($"ProductAsync Received:{x}, {y}");
            return x * y;
        }
    }
}

4.1.3. Unity側で API を呼ぶコードを実装する

SampleController に SampleService を呼び出すコードを追加します。
下記のコードをコピペして上書きしてください。

using Grpc.Core;
using MagicOnion.Client;
using Sample.Shared.Services;
using UnityEngine;
public class SampleController : MonoBehaviour
{
    private Channel channel;
    private ISampleService sampleService;

    void Start()
    {
        this.channel = new Channel("localhost:12345", ChannelCredentials.Insecure);
        this.sampleService = MagicOnionClient.Create<ISampleService>(channel);

        this.SampleServiceTest(1, 2);
    }

    async void OnDestroy()
    {
        await this.channel.ShutdownAsync();
    }

    async void SampleServiceTest(int x, int y)
    {
        var sumReuslt = await this.sampleService.SumAsync(x, y);
        Debug.Log($"{nameof(sumReuslt)}: {sumReuslt}");

        var productResult = await this.sampleService.ProductAsync(2, 3);
        Debug.Log($"{nameof(productResult)}: {productResult}");
    }
}

4.1.4. API 通信の動作確認

まずはサーバーを起動します。

ですが、その前にソリューションのスタートアッププロジェクトをサーバー側のプロジェクトにしておきます。
Sample.Server を右クリックして、スタートアッププロジェクトに設定をクリックします。
image.png

これによって、普段は Unity にアタッチ と表示されていたボタンが Sample.Server の表示に代わります。
image.png

このボタンを押すとサーバーを起動することができます。
image.png
※スタートアッププロジェクトを元に戻す場合は Assembly-CSharp を右クリックしてスタートアッププロジェクトに指定します。
※Unity にアタッチしつつサーバーを起動したい場合はマルチスタートアッププロジェクトを使用します。(後述)

続いて、サーバーを起動した状態で Unity の Scene を再生します。
image.png

Unity の Console にログが表示されました。
image.png

サーバー側も API が呼ばれたログが表示されました。
image.png

4.2. リアルタイム通信

続いてリアルタイム通信を試してみます。

4.2.1. Unity 側で API の定義を作る

普通の API 通信と同じく、まずは API の定義から作ります。
Assets\Scripts\ServerShared\Hubs 以下に SampleHub スクリプトを作ります。
image.png

SampleHub の中身は以下をコピペして保存してください。
今回はゲームにログイン、チャットで発言、位置情報を更新、ゲームから切断、という4つの API を作ります。

using MagicOnion;
using Sample.Shared.MessagePackObjects;
using System.Threading.Tasks;
using UnityEngine;

namespace Sample.Shared.Hubs
{
    /// <summary>
    /// CLient -> ServerのAPI
    /// </summary>
    public interface ISampleHub : IStreamingHub<ISampleHub, ISampleHubReceiver>
    {
        /// <summary>
        /// ゲームに接続することをサーバに伝える
        /// </summary>
        Task JoinAsync(Player player);
        /// <summary>
        /// ゲームから切断することをサーバに伝える
        /// </summary>
        Task LeaveAsync();
        /// <summary>
        /// メッセージをサーバに伝える
        /// </summary>
        Task SendMessageAsync(string message);
        /// <summary>
        /// 移動したことをサーバに伝える
        /// </summary>
        Task MovePositionAsync(Vector3 position);
    }

    /// <summary>
    /// Server -> ClientのAPI
    /// </summary>
    public interface ISampleHubReceiver
    {
        /// <summary>
        /// 誰かがゲームに接続したことをクライアントに伝える
        /// </summary>
        void OnJoin(string name);
        /// <summary>
        /// 誰かがゲームから切断したことをクライアントに伝える
        /// </summary>
        void OnLeave(string name);
        /// <summary>
        /// 誰かが発言した事をクライアントに伝える
        /// </summary>
        void OnSendMessage(string name, string message);
        /// <summary>
        /// 誰かが移動した事をクライアントに伝える
        /// </summary>
        void OnMovePosition(Player player);
    }
}

4.2.2 サーバー側で API を実装する

普通の API の実装の時と同じ要領で、Sample.Server 以下に Hubs フォルダを作り、その中に SampleHub.cs を作ります。
image.png

SampleHub.cs の中身は以下をコピペして保存してください。

using MagicOnion.Server.Hubs;
using Sample.Shared.Hubs;
using Sample.Shared.MessagePackObjects;
using System.Threading.Tasks;
using UnityEngine;

public class SampleHub : StreamingHubBase<ISampleHub, ISampleHubReceiver>, ISampleHub
{
    IGroup room;
    Player me;

    public async Task JoinAsync(Player player)
    {
        //ルームは全員固定
        const string roomName = "SampleRoom";
        //ルームに参加&ルームを保持
        this.room = await this.Group.AddAsync(roomName);
        //自分の情報も保持
        me = player;
        //参加したことをルームに参加している全メンバーに通知
        this.Broadcast(room).OnJoin(me.Name);
    }

    public async Task LeaveAsync()
    {
        //ルーム内のメンバーから自分を削除
        await room.RemoveAsync(this.Context);
        //退室したことを全メンバーに通知
        this.Broadcast(room).OnLeave(me.Name);
    }

    public async Task SendMessageAsync(string message)
    {
        //発言した内容を全メンバーに通知
        this.Broadcast(room).OnSendMessage(me.Name, message);
    }

    public async Task MovePositionAsync(Vector3 position)
    {
        // サーバー上の情報を更新
        me.Position = position;

        //更新したプレイヤーの情報を全メンバーに通知
        this.Broadcast(room).OnMovePosition(me);
    }

    protected override ValueTask OnDisconnected()
    {
        //nop
        return CompletedTask;
    }
}

4.2.3 Unity側で API を呼ぶコードを実装する

SampleController に SampleHub の各 API を呼び出すコードを追加します。
下記のコードをコピペして上書きしてください。

using Grpc.Core;
using MagicOnion.Client;
using Sample.Shared.Hubs;
using Sample.Shared.MessagePackObjects;
using Sample.Shared.Services;
using UnityEngine;

public class SampleController : MonoBehaviour, ISampleHubReceiver
{
    private Channel channel;
    private ISampleService sampleService;
    private ISampleHub sampleHub;

    async void Start()
    {
        this.channel = new Channel("localhost:12345", ChannelCredentials.Insecure);
        this.sampleService = MagicOnionClient.Create<ISampleService>(channel);
        this.sampleHub = StreamingHubClient.Connect<ISampleHub, ISampleHubReceiver>(this.channel, this);

        // 普通の API の呼び出しはコメントアウトしておきます
        // 残しておいても問題はないです(リアルタイム通信と両方動きます)
        //this.SampleServiceTest(1, 2);

        this.SampleHubTest();
    }

    async void OnDestroy()
    {
        await this.sampleHub.DisposeAsync();
        await this.channel.ShutdownAsync();
    }

    /// <summary>
    /// 普通のAPI通信のテスト用のメソッド
    /// </summary>
    async void SampleServiceTest(int x, int y)
    {
        var sumReuslt = await this.sampleService.SumAsync(x, y);
        Debug.Log($"{nameof(sumReuslt)}: {sumReuslt}");

        var productResult = await this.sampleService.ProductAsync(2, 3);
        Debug.Log($"{nameof(productResult)}: {productResult}");
    }

    /// <summary>
    /// リアルタイム通信のテスト用のメソッド
    /// </summary>
    async void SampleHubTest()
    {
        // 自分のプレイヤー情報を作ってみる
        var player = new Player
        {
            Name = "Minami",
            Position = new Vector3(0, 0, 0),
            Rotation = new Quaternion(0, 0, 0, 0)
        };

        // ゲームに接続する
        await this.sampleHub.JoinAsync(player);

        // チャットで発言してみる
        await this.sampleHub.SendMessageAsync("こんにちは!");

        // 位置情報を更新してみる
        player.Position = new Vector3(1, 0, 0);
        await this.sampleHub.MovePositionAsync(player.Position);

        // ゲームから切断してみる
        await this.sampleHub.LeaveAsync();
    }

    #region リアルタイム通信でサーバーから呼ばれるメソッド群

    public void OnJoin(string name)
    {
        Debug.Log($"{name}さんが入室しました");
    }

    public void OnLeave(string name)
    {
        Debug.Log($"{name}さんが退室しました");
    }

    public void OnSendMessage(string name, string message)
    {
        Debug.Log($"{name}: {message}");
    }

    public void OnMovePosition(Player player)
    {
        Debug.Log($"{player.Name}さんが移動しました: {{ x: {player.Position.x}, y: {player.Position.y}, z: {player.Position.z} }}");
    }

    #endregion
}

4.2.4 リアルタイム通信の動作確認

普通の API 通信の動作確認と同じ要領でサーバーを起動し、その後で Unity で Scene を再生します。

Unity の Console にログが表示されました。
image.png

これで普通の API 通信とリアルタイム通信の両方の動作確認ができました。

5. IL2CPP対応(コードジェネレーターによるコード生成)

※この方法は現在 Windows にのみ対応しています。
 Mac の場合は bash でコードジェネレーターを叩くなどの別の方法を使用してください。

UnityEditor 上で動かすなら今のままでも問題ないのですが、IL2CPP を使う場合(例えば Platform を iOS にしたとき)はこのようなエラーが発生します。
image.png

IL2CPP は動的なコード生成に対応していないため、コードジェネレーターを使用して事前に必要なコードを生成する必要があります。

MagicOnion と MessagePack for C# のコードジェネレーターは入手済みでしたので、それを UnityEditor 上からワンぽちで実行できるようにします。

Assets\Editor 以下に MenuItems スクリプトを作ります。
image.png

MenuItems の中身は以下をコピペして保存します。
プロジェクトの Path をハードコードしている箇所がありますので、そちらは自分の環境にあわせて書き換えてください。

#if UNITY_EDITOR
using System.Diagnostics;
using UnityEditor;
using UnityEngine;

public class MenuItems : MonoBehaviour
{
    [MenuItem("MagicOnion/CodeGenerate")]
    private static void GenerateFormatters()
    {
        // Generate MagicOnion code.
        ExecuteMagicOnionCodeGenerator();

        // Generate MessagePack code.
        ExecuteMessagePackCodeGenerator();
    }


    private static void ExecuteMagicOnionCodeGenerator()
    {
        UnityEngine.Debug.Log($"{nameof(ExecuteMagicOnionCodeGenerator)} : start");

        var exProcess = new Process();

        var rootPath = Application.dataPath + "/../..";
        var filePath = rootPath + "/GeneratorTools/MagicOnion.UniversalCodeGenerator/bin/moc";
        var exeFileName = "";
#if UNITY_EDITOR_WIN
        exeFileName = "/win-x64/moc.exe";
#elif UNITY_EDITOR_OSX
        exeFileName = "/osx-x64/moc";
#elif UNITY_EDITOR_LINUX
        exeFileName = "/linux-x64/moc";
#else
        return;
#endif

        var psi = new ProcessStartInfo()
        {
            CreateNoWindow = true,
            WindowStyle = ProcessWindowStyle.Hidden,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            FileName = filePath + exeFileName,
            Arguments = $@"-i ""{rootPath}/Sample.Server/Sample.Server.csproj"" -o ""{Application.dataPath}/Scripts/Generated/MagicOnion.Generated.cs""",
            // Pathの「Sample.Server」と「Sample.Server.csproj」は実際のものに書き換えてください
        };

        var p = Process.Start(psi);

        p.EnableRaisingEvents = true;
        p.Exited += (object sender, System.EventArgs e) =>
        {
            var data = p.StandardOutput.ReadToEnd();
            UnityEngine.Debug.Log($"{data}");
            UnityEngine.Debug.Log($"{nameof(ExecuteMagicOnionCodeGenerator)} : end");
            p.Dispose();
            p = null;
        };
    }


    private static void ExecuteMessagePackCodeGenerator()
    {
        UnityEngine.Debug.Log($"{nameof(ExecuteMessagePackCodeGenerator)} : start");

        var exProcess = new Process();

        var rootPath = Application.dataPath + "/../..";
        var filePath = rootPath + "/GeneratorTools/MessagePackUniversalCodeGenerator";
        var exeFileName = "";
#if UNITY_EDITOR_WIN
        exeFileName = "/win-x64/mpc.exe";
#elif UNITY_EDITOR_OSX
        exeFileName = "/osx-x64/mpc";
#elif UNITY_EDITOR_LINUX
        exeFileName = "/linux-x64/mpc";
#else
        return;
#endif

        var psi = new ProcessStartInfo()
        {
            CreateNoWindow = true,
            WindowStyle = ProcessWindowStyle.Hidden,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            FileName = filePath + exeFileName,
            Arguments = $@"-i ""{Application.dataPath}/../Assembly-CSharp.csproj"" -o ""{Application.dataPath}/Scripts/Generated/MessagePack.Generated.cs""",
        };

        var p = Process.Start(psi);

        p.EnableRaisingEvents = true;
        p.Exited += (object sender, System.EventArgs e) =>
        {
            var data = p.StandardOutput.ReadToEnd();
            UnityEngine.Debug.Log($"{data}");
            UnityEngine.Debug.Log($"{nameof(ExecuteMessagePackCodeGenerator)} : end");
            p.Dispose();
            p = null;
        };
    }
}
#endif

すると Unity Editor のメニューに MagicOnion が追加されます。
CodeGenerate ボタンを押してみます。
image.png

コード生成が完了すると以下のようなログが出力されます。
image.png

実際に生成されたコードは Generated フォルダに保存されています。
※これを触る必要はありません。
image.png

最後にこのコードが Scene 起動時に Register されるようにします。

Scripts フォルダに C# Script を作り、名前を InitialSettings とします。
image.png

InitialSettings の中身は下記をコピペして保存します。

using MagicOnion.Resolvers;
using MessagePack.Resolvers;
using MessagePack.Unity;
using UnityEngine;

namespace Assets.Scripts
{

    class InitialSettings
    {
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
        static void RegisterResolvers()
        {
            CompositeResolver.RegisterAndSetAsDefault
            (
                UnityResolver.Instance,
                MagicOnionResolver.Instance,
                GeneratedResolver.Instance,
                BuiltinResolver.Instance,
                PrimitiveObjectResolver.Instance
            );
        }
    }
}

これで IL2CPP の環境でも動くようになります。

6. マルチスタートアッププロジェクトについて

途中で説明を割愛したマルチスタートアッププロジェクトの利用方法です。

ソリューション を右クリックして、スタートアッププロジェクト の設定をクリックします。
image.png

マルチスタートアッププロジェクト にチェックをいれ、Assembly-CSharpSample.Server のアクションを 開始 にして OK を押します。
image.png

この状態で 開始 を押すと、Unityにアタッチしながらサーバーを起動することができます。
image.png

7. 後書きと参考にさせていただいた記事などへのリンク

こんなに長い記事を最後まで読んでいただいてありがとうございます。
少しでも役に立つことがあれば幸いです。

環境構築に成功して、より技術的な内容や実践的なコードが必要になった際は下記の記事などがおすすめです。

_y_minami
C# .NET Unity Azureが好きでその辺りのお仕事をしてます。 趣味はゲーム制作とお菓子作りと紅茶。 日本紅茶協会認定ティーアドバイザー。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした