Liberent-Dev’s blog

株式会社リベル・エンタテインメントのテックブログです。

ASP.NET Coreを初めて触りつつMemory Packを試してみた for Unity

こんにちは!
システム開発部のK.Mです。

前置き

liberent-dev.hatenablog.com

前回のMemoryPackの記事にて、APIとwebにてMemoryPackを使用したシリアライズ・デシリアライズの実装をしました。

今回はAPIをそのまま使用して、UnityにてMemoryPackデータをデシリアライズしてコンソールや画面上に表示するということをやっていきます。

前回同様、サーバ・バックエンドは触ったことはあるけど、Unity/C#は初めて使う人を想定しております。

開発環境

Unityのインストール

MemoryPackがUnityのバージョン2021.3以上のものが対象になっているため、2021.3以上のものをインストールしていきます。

Unityを直接インストールせずに、UnityHubというものをインストールしたうえで2021.3以上のものをUnityHubから指定してインストールする手順となります。

  1. Unityのダウンロードページへアクセスします。
  2. Mac用のダウンロードのリンクを選択します。(Mac以外であれば、各OS用のリンクを選択してください。)
  3. UnityHubSetup.dmgがダウンロードされるので、ダウンロード後に実行します。
  4. セットアップ画面が表示されるので[Agree]を選択します。
  5. 処理中画面が出たのち、少し待つとApplicationsへドラッグ&ドロップする画面が表示されるので、UnityHubアイコンをApplicationsへドラッグ&ドロップします。
  6. コピー中画面が表示されるので、少し待つとApplicationsにUnityHubがインストールされます。
  7. UnityHubをインストールする画面を閉じます。
  8. Launchpadを開いて、UnityHubがインストールされていることを確認、選択して起動します。
    (もし起動時に下記のような開いても良いかの確認ダイアログが出た場合は[開く]を選択してください)
  9. UnityHub起動後にUnityのアカウントにログインをする画面が表示されるため、Unityアカウントを作成します。
    UnityHub画面の[アカウントを作成]のリンクか、こちらからUnityIDを作成してください。
  10. UnityID作成後、UnityHubにてサインインします。
  11. サインイン後に初回起動時にエディターインストール画面が表示されるので2021.3以上のバージョンになっているのを確認して、[Unityエディターをインストール]ボタンを選択しインストールを行います。
  12. 初回起動のインストール画面からインストールをすると、Androidビルドに必要なものがインストールされていないので、下記の操作して別途インストールする必要があります。
    1. エディター一覧の右側にある歯車ボタンを選択し、モジュールを加えるを選択します。
    2. 追加するモジュール一覧画面が表示されるので、Android Build Supportにチェックを入れて次へを選択します。
    3. 利用規約画面が表示されるので同意部分のチェックボタンをONにして[インストール]ボタンを選択するとインストールが開始されるので、終了するまで待ちます。
    4. インストール完了後、UnityHubのエディター一覧にてAndroidの表示があればOKです。

上記でUnity関連のインストールが完了となります。

Unityでプロジェクト作成

UnityHubにてテンプレートでプロジェクトが作成出来るため、下記手順にてプロジェクトを作成します。

  1. 「新しいプロジェクト」ボタンを選択します。
  2. テンプレートを選択する画面が表示されるので、上のあるエディターバージョンが先ほどインストールした2021.3以上のもになっているか、テンプレートが2D(コア)を選択、プロジェクト名・保存場所は任意の場所に設定して、「プロジェクトを作成」ボタンを選択します。
  3. プロジェクトの作成とUnityが起動するまで少し時間がかかるため、待ちます。

Unityの簡単な画面説明

①Hierarchy
ゲームオブジェクト、モデル、カメラなどUnityを構成するための各種オブジェクト一覧が表示される部分

②SceneビューとGameビュー
作成している画面が表示される部分

③Inspector
ゲームオブジェクトなどのUnityを構成するための各種オブジェクトの設定画面が表示される部分

④プロジェクトとコンソール
ソースファイルや画像ファイルなど、プロジェクトを構成するファイル一覧が表示される部分
(コンソールタブを変更すると実行時やビルド時などのログが表示される部分でもあります。)

詳しい操作方法に関しては今回は致しませんが、上記の場所が分かっていればこの後の操作は問題なく出来ます。詳しい内容はこちらに記載があります。
また、表示位置に関しては色々調整が可能です。

UnityにMemoryPackをインストール

インストール方法ですが、

  • Unity上にてgitのURLを設定してインストール
  • こちらから.unitypackageのファイルをDLしてインストール

上記の2パターン存在しますが、前者の場合System.Runtime.CompilerServices.Unsafe/6.0.0という別のパッケージを別途インストールする必要があるため、今回はまとめてインストール可能な.unitypackageでの手順を記載します。

  1. こちらからAPIサーバ側の実装と同じバージョンの.unitypackageをDLしておきます。
  2. UnityからDLしたMemoryPackの.unitypackageをインストールします。
    起動しているUnityのメニューから「Assets」→「Import Package」→「Custom Package...」を選択します。
  3. ファイル選択画面が表示されるので、先ほどDLした.unitypackageを選択し[open]ボタンを選択します。
  4. 下記のようにどのファイルをインポートするのかを選択する画面が表示されるので、[All]ボタンを押して全てチェックが入っていることを確認して、[Import]ボタンを選択します。

上記操作でインポートすることで、UnityにてMemoryPackが使えるようになります。

画面構築

ここからはUnityからAPIサーバへアクセスして取得したMemoryPackでシリアライズされたレスポンスデータをデシリアライズして表示する部分を対応していきます。

手順通りにプロジェクトを作成している場合は、画面左にあるHierarchySampleSceneMain Cameraが存在しています。
Hierarchyにて右クリックしてメニューを表示して、「UI」→「Text - TextMeshPro」を選択します。

HierarchyCanvasText(TMP)EventSystemが作成されます。

この段階では下記のようにScene画面にてCanvasの位置がMain Cameraで表示する場所から大きくはみ出しているため、CanvasMain Cameraに合わせる設定を行います。

  1. HierarchyにあるCanvasを選択すると、画面右にあるInspectorCanvasの設定項目が表示されます。
  2. Canvasの設定項目にあるRender ModeScreen Space - OverlayになっているのでScreen Space - Cameraに変更します。
  3. 変更後にRender Cameraという項目が表示されるので、None(Camera)になっている部分の丸い部分を選択します。
  4. オブジェクトを選択する画面が表示されるので、そこからMain Cameraを探して選択します。
  5. 正常に設定されると、Render Cameraの設定内容がMain Camera(Camera)になるのと、先ほどのScene画面にてCanvasCameraの枠が一致します。

また、Text追加時に別ダイアログで下記のようなものが表示されている場合は、[Import TMP Essentials]ボタンして、TextがScene画面で表示されるようになります。

このままですと、Textの表示位置が画面下側になっているのでTextの位置を移動させておきましょう。

Unityの再生ボタンを押して、Game画面にてテキストが表示されていることを確認してください。

確認が終わったら、再度再生ボタンを押して停止させておきます。

更に今後の操作用にTextの名前を変更しておきます。

  1. HierarchyにあるText(TMP)を選択します。
  2. Inspectorに設定画面が表示されるので一番上のある名前を変更します。
    赤枠部分が入力項目になっているので、TxtMsgに変更します。


APIサーバへの通信

ここからはAPIサーバへ通信してレスポンスを取得する処理を追加していきます。

まずはAPIサーバへ通信する処理を書くC#ファイルを置くフォルダを作成します。
画面下にあるProjectタブ内にある、Assetsを選択します。

Assets内にあるフォルダが表示されている場所で右クリックをしてメニューから「Create」→「Folder」を選択してScriptsフォルダを作成します。

作成したScriptsフォルダをダブルクリックで選択することでScriptsフォルダ内に移動します。
同じく、右クリックしてメニューから「Create」→「C# Script」を選択すると、C#のファイルが作成されてファイル名の入力を求められるので、適当なファイル名を入力してください。

その後、作成したC#ファイルをダブルクリックで選択することでVisual Studioが立ち上がります。

StartとUpdateのメソッドしかないクラスのソースになっていますので、下記のように処理を追加していきます。
(下記ソースのコメントにて//Addとなっている部分が追加部分となります。)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking; // Add

public class MemoryPackSample : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(GetHttp());  // Add
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    // Add(GetHttp()全て)
    IEnumerator GetHttp()
    {
        UnityWebRequest request = UnityWebRequest.Get("http://localhost:5265/WeatherForecast");
        yield return request.SendWebRequest();

        if(request.result != UnityWebRequest.Result.Success)
        {
            Debug.Log(request.error);
        }
        else
        {
            if(request.responseCode == 200)
            {
                Debug.Log(request.result);
                Debug.Log(request.downloadHandler.data);
                Debug.Log(request.downloadHandler.text);
            }
        }
    }
}

前回記事と同じ操作でAPIサーバを立ち上げて、Unityを実行した後に画面下にあるConsoleタブを選択するとDebug.Logで出力した内容が表示されています。
今回はAPIサーバへのアクセス時に指定していないので、Json形式でレスポンスが返却されてきています。

MemoryPack実装

ここからはMemoryPack部分の対応を行なっていきます。
前回作成しているWeatherForecast.csのファイルを今回のUnityで作成したプロジェクトの「Assets」 -> 「Scripts」配下へコピーします。

ここで一点問題が存在しています。
Unityが対応している.Netバージョンの問題でDateOnly(.Net6と7のみ)構造体が使えないため、エラーが発生してしまいます。
そのため、APIサーバの実装も合わせて修正を行なっていきます。
APIサーバ側にてUnityの.Netバージョンを考慮せずに実装するとIN/OUT部分でエラーになってしまうので要注意事項です。)

サーバ側の修正

using MemoryPack;

namespace TestMemoryPack;

[MemoryPackable]
public partial class WeatherForecast
{
    public DateTime Date { get; set; } // ここをDateOnlyからDateTimeに修正
    public int TemperatureC { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    public string? Summary { get; set; }
}
    [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get()
    {
        return Enumerable.Range(1, 8).Select(index => new WeatherForecast
        {
            Date = DateTime.Now, // ここを修正
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

Unity側の修正

コピーしたWhatherForecast.csに関してはサーバと同じくDateOnlyからDateTimeへ変更しておきます。

MemoryPackを取得するヘッダーを追加する処理を追加しておきます。

        UnityWebRequest request = UnityWebRequest.Get("http://localhost:5265/WeatherForecast");
        request.SetRequestHeader("Content-Type", "application/x-memorypack");  // Add
        request.SetRequestHeader("Accept", "application/x-memorypack"); // Add
        yield return request.SendWebRequest();

MemoryPackのレスポンスはバイナリのため、Debug.Logでそのまま表示すると何も表示されないので、Webと同じようにデシリアライズした後は、個別に表示する処理を入れ込みます。

    IEnumerator GetHttp()
    {
        UnityWebRequest request = UnityWebRequest.Get("http://localhost:5265/WeatherForecast");
        request.SetRequestHeader("Content-Type", "application/x-memorypack");
        request.SetRequestHeader("Accept", "application/x-memorypack");
        yield return request.SendWebRequest();

        if(request.result != UnityWebRequest.Result.Success)
        {
            Debug.Log(request.error);
        }
        else
        {
            if(request.responseCode == 200)
            {
                Debug.Log(request.result);
                Debug.Log(request.downloadHandler.data);
                Debug.Log(request.downloadHandler.text);
                var val = MemoryPackSerializer.Deserialize<WeatherForecast[]>(request.downloadHandler.data); // Add
                foreach (WeatherForecast w in val)
                {
                    Debug.Log(w.Date.ToString() + ":" + w.TemperatureC + ":" + w.TemperatureF + ":" + w.Summary); // Add
                }
            }
        }
    }

動作確認

前回記事と同じ操作でAPIサーバを起動してから、Unity上で実行ボタンを押して実行します。
Unity上のコンソール画面で結果が表示されていればOKです。

Androidでの確認方法

画面上に表示されるように修正

現状のままAndroidで動かしてもコンソールに出力されているものは、画面には表示されないので画面上に表示するように対応します。

画面構築の際に作成しておいたUIのTextを使って行く形となります。
ソースを下記のように修正します。

using System.Collections;
using System.Collections.Generic;
using MemoryPack;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Networking;

public class MemoryPackSample : MonoBehaviour
{
    public TMPro.TextMeshProUGUI TxtMsg; // Add

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("start");
        StartCoroutine(GetHttp());
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    IEnumerator GetHttp()
    {
        Debug.Log("gethttp");
        UnityWebRequest request = UnityWebRequest.Get("http://localhost:5265/WeatherForecast");
        request.SetRequestHeader("Content-Type", "application/x-memorypack");
        request.SetRequestHeader("Accept", "application/x-memorypack");
        yield return request.SendWebRequest();

        if(request.result != UnityWebRequest.Result.Success)
        {
            Debug.Log(request.error);
        }
        else
        {
            if(request.responseCode == 200)
            {
                Debug.Log(request.result);
                Debug.Log(request.downloadHandler.nativeData);
                Debug.Log(request.downloadHandler.data);
                Debug.Log(request.downloadHandler.text);
                var val = MemoryPackSerializer.Deserialize<WeatherForecast[]>(request.downloadHandler.data);
                var txtMsg = ""; // Add
                foreach (WeatherForecast w in val)
                {
                    Debug.Log(w.Date.ToString() + ":" + w.TemperatureC + ":" + w.TemperatureF + ":" + w.Summary);
                    txtMsg += w.Date.ToString() + ":" + w.TemperatureC + ":" + w.TemperatureF + ":" + w.Summary + "///";  // Add
                }
                TxtMsg.text = txtMsg; // Add
            }
        }
    }
}

Inspectorのソースファイルの設定画面にTxtMsgという項目が増えているので、作成したTextを設定します。

TxtMsgの項目でNoneとなっている横の○部分を選択し、オブジェクト一覧を表示して作成したTxtMsgを選択します。

選択後に、下記のようなNoneではなくTextのオブジェクトが設定されていればOKとなります。

一度確認のためUnity上で動くこと確認しておきます。

Androidなどの外部から接続出来るようにする

現状のままのAPIサーバですとlocalhost:5265というUrlで起動するため、AndroidにapkをインストールしてアプリからアクセスしてもPCへは繋がりませんので、urlの設定を変更しておきます。

APIサーバ側のソースのlaunchSetting.jsonにUrlの設定項目があるので下記のように変更します。

  "profiles": {
    "http": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "http://*:8080",  // Change
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
この設定でAPIサーバが立ち上がっているIPへアクセス可能になります。
また、アプリ側でlocalhostのままだと繋がりませんので、接続先の情報はlocalhostから変更しておく必要があります。
下記、修正例はAPIサーバが起動しているPCのIPが192.168.1.1の場合となりますので、各自環境に合わせて変更をお願いします。

    IEnumerator GetHttp()
    {
        Debug.Log("gethttp");
        UnityWebRequest request = UnityWebRequest.Get("http://192.168.1.1:8080/WeatherForecast");  // Change
        request.SetRequestHeader("Content-Type", "application/x-memorypack");
        request.SetRequestHeader("Accept", "application/x-memorypack");
        yield return request.SendWebRequest();

        if(request.result != UnityWebRequest.Result.Success)
        {
            Debug.Log(request.error);
        }
        else
        {
            if(request.responseCode == 200)
            {
                Debug.Log(request.result);
                Debug.Log(request.downloadHandler.nativeData);
                Debug.Log(request.downloadHandler.data);
                Debug.Log(request.downloadHandler.text);
                var val = MemoryPackSerializer.Deserialize<WeatherForecast[]>(request.downloadHandler.data);
                var txtMsg = "";
                foreach (WeatherForecast w in val)
                {
                    Debug.Log(w.Date.ToString() + ":" + w.TemperatureC + ":" + w.TemperatureF + ":" + w.Summary);
                    txtMsg += w.Date.ToString() + ":" + w.TemperatureC + ":" + w.TemperatureF + ":" + w.Summary + "///";
                }
                Debug.Log(txtMsg);
                TxtMsg.text = txtMsg;
            }
        }
    }

Unityの初期設定

Android用のインストール媒体である、apkファイルを作るための設定をしていきます。
メニューから「File」→「Build Settings...」を選択します。

Build Settingsの画面が表示されるので、Platform一覧にあるAndroidを選択して[Switch Platform]ボタン選択すると、Android対応の処理が走りますので終わるまで待ちます。
処理が終わってBuild Settingsの画面のPlatformのAndroidにUnityのマークが付いていれば変更完了です。

[Build]ボタンを押すと、apkファイルの保存先を設定する画面が表示されるので任意の場所とフィアル名入力して、[Save]ボタンを押すとビルドが始まります。
問題が無ければapkが作成されます。
何かしら問題があると、Unityのコンソールにエラー内容が表示されるので、エラー内容を元に修正をしていきます。

apkインストール・起動

作成されたapkをAndroidへ転送し、インストールします。
Androidの設定で提供元不明アプリがインストール出来ない設定になっている場合だと、警告が出ますので提供元不明アプリでもインストール可能な設定に変更しておきます。

インストール後、起動前にサーバがローカル環境で動いているので、AndroidにてWi-Fiなどで同じネットワークに接続出来るようにしておきます。
インストールしたアプリを起動してUnityと同じような表示が出ていれば完成です。

最後に

リベル・エンタテインメントでは、このような最新技術などの取り組みに興味のある方を募集しています。
もしご興味を持たれましたら下記サイトにアクセスしてみてください。
https://liberent.co.jp/recruit/