つくるの大好き。

つくるのが大好きな人の記録。

MacBook Pro (M1 Pro)のUnity Android Build ベンチマーク

お久しぶりです!
MacBook Pro 16-inch が届きました!CPUはM1 Proを選択しています。
とにかく物凄い性能だと言われていますが自分がよく使うユースケースでどれくらい凄いのかベンチマークを取ってみました。

f:id:peugeot-106-s16:20211101131246j:plain
MacBook Pro 16-inch

1.ユースケース

僕は普段Android / iOS用のライブラリやSDK、ツールの設計開発を行なっています。
開発言語はC++(Android Studio, Xcode)とUnityです。
カメラ系のものなので基本的に低レイヤのC++実装部で処理を行ない、高レイヤのUnityとやり取りをします。
動作確認は基本的にはUnityでビルドを行ない、実機へアプリを転送して行うことになります。 そのビルド時間はだいたい5分ぐらいの印象です。

この 5分というのはその間に他のことをするには短いけれど待つには長い です。

絶妙にどうしようもない時間です(笑)
僕の場合、チームからプルリクエストが送られてくるのでソースコードレビューをすると共に、動かしてみないとわからない変更については都度チェックアウトしてプロジェクトを開き、ビルドして動作確認をします。
その度にビルド5分です。
動かした瞬間うまく動かなかったりしたらそのビルドは無駄になり、フィードバックして直ったと連絡を受けるとまたビルドです。
仕事時間の大半がUnityビルドに費やされると言っても過言ではない日があるので
1分1秒でもビルドは短くしたい のです。
僕は限りある人生の時間をもっと有効に使いたいんです!

それで今回は UnityプロジェクトをオープンしてAndroidアプリをビルドする というユースケースベンチマークを取ってみたいと思います。

サンプルに使用するUnityプロジェクトはこちら。

github.com

2.比較環境

A: G-Tune (NG-N-i5750) B: MacBook Pro (13-inch, 2020) C: MacBook Pro (16-inch, 2021)
CPU Core i7-9750H 2.60GHz M1 M1 Pro
Memory 32GB 16GB 32GB
Storage SSD SSD SSD
OS Windows 11 Pro 21H2 macOS Monterey 12.0.1 macOS Monterey 12.0.1
価格 約30万円 約20万円 約40万円

マウスコンピュータのG-Tune ゲーミングノートをベンチマークの基準としました。これは約2年前に購入ししたものです。
MacBook Pro はM1 Proの機体に加えM1を搭載した13-inch も用意しました。M1 Proになるとどれだけ性能か変わるか楽しみですね!!

3.比較結果

というわけで、結果どん!
かかった時間に加え、基準であるG-Tune対して削減された秒数、高速化率を求めています。
また、同じようにM1に対してM1 Proで削減された秒数、高速化率も求めています。
(なお処理時間計測はストップウォッチで行なっているため若干の誤差はあります。)

f:id:peugeot-106-s16:20211101141313p:plain
比較結果

実行したアクションの流れは下記です。

  • Libraryフォルダなどのテンポラリフォルダを削除した状態でプロジェクトを開く x 3回
  • ビルドプラットフォームをAndroidに切り替える
  • ビルドする(インクリメンタルビルドが有効なので、初回と2-5回目の平均を分けて求める)
  • Unityのバージョンを変更してビルドする(初回と2-5回目の平均を別に求める)

4.考察

4-1.Unity2020からUnity2021に移行すると

  • 初回ビルドは、M1 Pro(C)以外は2020のほうが速い。インクリメンタルビルドの準備に時間がかかるのかも。
  • 2回目以降のビルドは物凄く速くなる。所要時間は半分程度になる。

同一プロジェクトを繰り返しビルドすることが多い人はUnity2021を使えばそれだけで働き方改革ができる!

4-2.G-Tune(A)からM1(B)に移行すると

  • Unity Projectオープンは133%の高速化
  • 初回ビルドは160%の高速化
  • 2回目以降のビルドは125-145%の高速化

30万円のPCから20万円のPCへ乗り換えて総じて150%近くの時間短縮ができる。なんで乗り換えないの!?レベル

4-3.G-Tune(A)からM1 Pro(C)に移行すると

  • Unity Projectオープンは161%の高速化
  • 初回ビルドは165-172%の高速化
  • 2回目以降のビルドは130-149%の高速化

30万円のPCから40万円のPCへ乗り換えて総じて160%近くの時間短縮ができる。凄い!凄いけど投資もそれなりだよね!

4-4.M1(B)からM1 Pro(C)に移行すると

  • Unity Projectオープンは141%の高速化
  • 初回ビルドは114-129%の高速化
  • 2回目以降のビルドは107%の高速化

プロジェクトを開いてからの初回ビルドは速くなった!けどビルドはそんなに変わらん?

5.まとめ

以上ベンチマーク結果をまとめると、僕のユースケースにプロジェクトオープンと初回ビルドが高速化されるM1 Pro 搭載 MacBook Pro はベストマッチだということがわかりました!

ただ正直多くのUnityでビルドする方が M1 から M1 Pro / Max に乗り換えるほどでもない ということも見えました。
ただ今回はUnity以外のアプリは終了させたクリーンな環境でテストしたので、ブラウザで多くのタブが開き、Android StudioXcodeなどの別のツールを立ち上げメモリを消費した状態ではもっと大きな差が出そうです。
実際、ビルド以外の普通の操作のキビキビ感はM1 Pro の恩恵だなと感じています。

現在非M1のPCをお使いの方はM1に乗り換えない理由は無いと思いました。

うまくて速くて安いからです

また驚くべき事実として、まだUnityやそこから呼ばれるJava, GradleなどのツールはM1に最適化されていません。
追って最適化が行われるとまた劇的に速くなると期待できます。これはやばい(語彙).

f:id:peugeot-106-s16:20211101174602p:plain.

M1 Pro / M1 Max 搭載のMacBook Proについてはベンチマーク記事がたくさん出ていますが自分が実用するユースケースとは乖離があるので自分で調べてみるとさまざまな気づきがありました。
また、Unityでデバイス向けIL2CPPビルドを多く行う方にとっても参考にしてもらえる情報になるのではないかと思います。

さて、じゃあ節約できるようになった時間でどれだけコストが減るのかというお金面はあまり触れていません。
次回、じゃあ乗り換えると何円お得なの!?っていうのを計算して記事を書く予定です。

Azure Spatial Anchors のすぐビルドできるプロジェクト

f:id:peugeot-106-s16:20201206115603p:plain

What's?

Azure Spatial Anchors の勉強をしたのですが、チュートリアルをなぞってビルドしてゆくにはそれなりに時間がかかって大変でした(笑)
すぐビルドできるプロジェクトを公開したので良かったらお使いください。

下記が特徴です。

  • アカウント情報を設定すればすぐビルドできます。
  • UWP(Windows Mixed Reality), Android with ARCore, iOS with ARKit の3プラットフォームに対し、プラットフォーム切り替えだけでビルドでき、動作します
  • Coarse Relocalizationに対応しています
    (うまく動かせていないプラットフォームもあります。リポジトリのReadmeを参照ください)
  • 操作がシンプルなので、営業してくれる人に渡すデモアプリ等に使うと便利です(生々しいw)

github.com

Demo videos

参考リンク

宮浦さん、ありがとうございます! qiita.com

Deep dive into the ARCore Depth Lab #1

ARCore Depth API を学ぶにはサンプルコードとして公開されている ARCore Depth Lab サンプルを見るのが良いのですが、このサンプルは完成度も高くコード量も多く、抽象化もされているのでどこで処理が繋がっているのかわかりにくいところもあります。それで、サクッと理解しようと思うと少し大変だと思うかも知れません。
ぼくが内部解析をした過程を書きますのでこれから学ぶという方の参考になれば嬉しいです!

github.com

なおこのサンプルはGoogle Play Storeからもダウンロード可能です。 単純に遊ぶだけでも楽しめる高度なサンプルプログラムなので、Deep Diveすると色々勉強になりました!

play.google.com

シーン構成

/Assets/ARRealismDemos/DemoCarousel/Scenes/DemoCarousel シーンがエントリポイントになります。
これはBuild Settingsより確認できます。
そして、いくつかのサブシーンからこのサンプルは構成されていることもわかります。

f:id:peugeot-106-s16:20200726103056p:plain

シーン切り替え

DemoCarousel シーンはサブシーンをロードするためのボタンを持つシーンです。 f:id:peugeot-106-s16:20200726103943p:plain

このボタンからどのシーンをロードするかは、ボタンのGameObject配下のquadにアタッチされているScene ButtonコンポーネントのScene Nameフィールドに文字列で指定されています。
ボタンのキャプションとロードされるシーン名は一致していないのですが、マッピングポイントはここです。

f:id:peugeot-106-s16:20200726104417p:plain

また、ボタンにマッピングされていないデモシーンも多数あるのでマッピングを書き換えればそれらを呼び出すこともできますね。

Depthデータハンドリング(浅め)

このサンプルでは DepthSource コンポーネントがアタッチされているオブジェクトがDepthデータを読み取り、DepthTargetコンポーネントをもつオブジェクトにDepthデータを送るようになっています。
DepthTargetコンポーネントは複数のオブジェクトにアタッチでき、全てに対してDepthデータがTextureとして毎フレーム更新されます。

このDepthSourceやARCoreのカメラを含む有用なオブジェクトは /Assets/ARRealismDemos/Common/Prefabs/DepthARComonents プレファブとして用意されていますので、通常のアプリ開発ではこのプレファブをシーンに配置し、Depthデータを受け取りたいオブジェクトにDepthTargetコンポーネントをアタッチするだけでTextureデータとしてDepthデータを受け取ることができます。

例えばDepthEffects シーンでは下記のようにDepthMapVisualizationオブジェクトにDepthTargetコンポーネントがアタッチされ、Textureデータを受け取るようになっています。
f:id:peugeot-106-s16:20200726111115p:plain

Depthデータハンドリング(深め)

もう少し深く分け入って、Depthデータをどのように取得しているのか見てみましょう。

DepthData処理の実装クラス

DepthSource.Start() では"DepthDataSourceConfig"という名前のリソースをロードしています。
arcore-depth-lab/DepthSource.cs at d56cafdb2421513dcefaeac82d6eded9e9089aa9 · googlesamples/arcore-depth-lab · GitHub

DepthDataSourceConfigを見ると、DepthSourceClassNameフィールドに、クラス名が文字列で指定されています。 f:id:peugeot-106-s16:20200726111740p:plain
このMotionStereoDepthDataSourceクラスがDepthデータ取得を行う実装クラスとなります。
(ここを差し替えればダミーデータを差し込んだり、全く違うデバイスのDepthデータを突っ込めたり、、ワクワクしてきたw)

よって低レベルよりの処理はこちらを読んでゆけば読み解くことができます。

arcore-depth-lab/MotionStereoDepthDataSource.cs at master · googlesamples/arcore-depth-lab · GitHub

つづく

というところで、これから出かけるで今回はここまでです(笑)
次回、MotionStereoDepthDataSource.csやShader周りにDeep dive してゆきたいと思います!
需要があれば、またお会いしましょうw!

SunnySideUp UnityChan を HoloLens2 の既存プロジェクトに入れて動かしてみた

SunnySideUp UnityChanがリリースされたというニュースを見てさっそく試してみました!
PCでは当たり前に動くのでHoloLens2に入れて動作させるまでの手順を書きます。

f:id:peugeot-106-s16:20200712171402p:plain

SunnySideUp UnityChan

unity-chan.com

UnityのHDRP (High Definition Render Pipeline)とURP(Universal Render Pipeline)によるレンダリングに対応した新世代のUnityちゃんアセットです。
HDRPはPC等のハイパフォーマンスな環境用、URPは様々なデバイスで動作できる高い互換性とクオリティを併せ持つレンダリングシステムです。

新世代というのも、HDRPやURPに切り替えると既存のシェーダーはそのまま使えません。全部悲しみの紫になります。
移行するには清水の舞台から飛び降りる覚悟が要りそうですが、魅力的なアセットが使えるとなれば飛べる気がする方も多いのではないでしょうか!

既存プロジェクトの更新作業

ベースプロジェクトはこちら。Candy Rock Star Unityちゃんが現実世界に出現するというぼくが始めてHoloLensで作ったアプリです。
4年以上前に作ったものですが、地道に最新環境にあわせてアップデートされており、MRTK2.4+HoloLens2で動作します。

github.com

このプロジェクトにSunnySideUp UnityChanのURP版を導入してみましょう。

パッケージのインストール

SunnySideUp UnityChan URPプロジェクトは下記のパッケージを利用しています。 同じバージョンのパッケージをインストールしましょう。

f:id:peugeot-106-s16:20200712162223p:plain

ただし、この中で下記のものはSunnySideUp UnityChan URPプロジェクトのPackagesフォルダ内に含まれているので、そこからコピーしてインストールしました。

  • Unity Chan Spring Bone
  • Universal Toon Shader for URP

ビルドを通すための修正
Unity Chan Spring BoneはUWPプロジェクトを書き出すときに大量のビルドエラーを出します。
これはUnity Editor用のスクリプトがビルド対象に含まれているからです。
パッケージ内のAssemly Definitionの設定を下記のようにEditor限定に変更する必要があります。

f:id:peugeot-106-s16:20200712171848p:plain

なお、このビルドエラーは別プロジェクトでAndroid ARM64(IL2CPP)ビルドをした時にも発生し、同様の対処が必要でした。

Add packages · SystemFriend/HoloLensUnityChan@9caee43 · GitHub

SunnySideUp UnityChanアセットのインストール

依存関係はフォルダ内で完結しているので、UnityChanSSUフォルダを丸ごとインポートすればOKです。

HoloLensUnityChanプロジェクトはURP対応ではないので、ここで UnityChanSSU_DynCol プレファブをシーンに配置しても何もレンダリングされません。
ここから色々ありましたw

URPに切り替える

URPのプロファイルを新規作成します。 f:id:peugeot-106-s16:20200712163650p:plain

設定は軽めに。   f:id:peugeot-106-s16:20200712163823p:plain

[Project Settings]-[Graphics]-[Scriptable Render Pipeline Settings]にアタッチします。   f:id:peugeot-106-s16:20200712164018p:plain

これでSunnySideUp UnityChanがレンダリングされるようになりました。

f:id:peugeot-106-s16:20200712164308p:plain

既存ShaderからURP対応のShaderへ切り替える

プロジェクトのレンダリングがURPに切り替わったところで、レガシーShaderを使っているCandy Rock Star Unityちゃんは悲しみの紫になります。
二股を掛けることは許されないのです。

地道にCandy Rock Star UnityちゃんのShaderを "Universal Render Pipeline/Toon" へ切り替えてゆきます。
このShaderは設定がめちゃくちゃたくさんあります!
マニュアルは読んでいないのですがハイライトが強すぎてエッジが真っ白になってしまっていたのを下記を0にすることで対応しました。

f:id:peugeot-106-s16:20200712165028p:plain

また、髪の毛のシャドウ部分の色がグレーになっておかしいと思いましたので、ここだけ色を設定しました。 f:id:peugeot-106-s16:20200712165148p:plain

また、Directional Lightを使うと光が強すぎて白飛びするので削除してPoint Lightを設置しました。

上記諸々の変更がこちらのコミットです。

Add Unity-chan SSU · SystemFriend/HoloLensUnityChan@949ab70 · GitHub

Unity EditorのGameビューにレンダリングされない

ここでPlayすると、Gameビューに編集中は行われていたレンダリングが行われず真っ黒になります。
これは [Project Settings]-[Player]-[XR Settings]にあるレガシィなシステムでURPを使う場合起こる現象らしいです。

https://forum.unity.com/threads/urp-in-hololens-app-changes-perspective-of-camera-in-game-view.874522/

レガシィを卒業して新しいXR Plug-in Frameworkを使いなさいということなので切り替えます。
そしてMRTKも XRSDK の接頭辞がつくプロファイルへの切り替えが必要です。
大変な大工事になっています!
寝る前にちょっとやてみようと思って始めた作業ですが、既に午前3時ですよ。

Unity - Manual: XR Plug-in Framework

こちらはトライアンドエラーで設定を変えていったので、詳細はGitHub上のプロジェクトをご覧ください。

Using the Unity XR SDK · SystemFriend/HoloLensUnityChan@a7c2d37 · GitHub

なんとか移行できた

というわけで動きました。

下記課題は残りました。
引き続き取り組んでゆきたいので良い情報があればぜひ教えてください。

  • MRTKのShaderがURPに切り替えと全て悲しみの紫になる。
  • HandInputが動いていないっぽい
  • HandMenuが息をしていないっぽい
  • キャラクターの足裏の高さが、Spatial Awareness で認識している床面より高い位置になっているっぽい
  • アプリのパフォーマンスは移行前と比べるとかなり重い

お知らせ

SunnySideUp UnityChan について詳しい説明がCGWORLD (シージーワールド) 2020年 08月号に掲載されているそうなので良かったらぜひ!
amzn.to

ARCore Depth API for Unity でdepth値をShaderで扱う

ARCore v1.18.0がリリースされ、Depth APIが使えるようになりました。 早速ハックしてみたいと思います(笑)

f:id:peugeot-106-s16:20200628225056j:plain

ARCore Depth API for Unityの説明はこちら developers.google.com

Depthデータへのアクセス

https://developers.google.com/ar/develop/unity/depth/overview#depth-maps

Frame.CameraImage.UpdateDepthTexture() から取得することができます。 Texture2Dでの提供です。

なお、CPUでデータを扱いたい場合はこちらが使えます。
https://github.com/google-ar/arcore-unity-sdk/blob/fbc21676ef0bb305c775fa3be593b8c46a62c211/Assets/GoogleARCore/SDK/Scripts/Api/Wrappers/FrameApi.cs#L352-L353

Depthデータのフォーマット

https://developers.google.com/ar/develop/unity/depth/overview#depth-map-format

ARCore Depth APIのデータフォーマットはDEPTH16形式と呼ばれるもので、13bitのdepth値(mm単位)と3bitのconfidence値(信頼性)を組み合わせた16bit値です。
ただしARCore Depth APIではconfidenceは常に0がセットされるようなので取得できないようです。

https://developer.android.com/reference/android/graphics/ImageFormat#DEPTH16

Textureデータのフォーマット

Frame.CameraImage.UpdateDepthTexture() で取得できるTextureのフォーマットはRGB565形式で16bitデータが格納されます。

https://github.com/google-ar/arcore-unity-sdk/blob/fbc21676ef0bb305c775fa3be593b8c46a62c211/Assets/GoogleARCore/SDK/Scripts/Api/Wrappers/FrameApi.cs#L269-L270

Shader内でdepth値を求める

ここまでの解説をまとめると、ARCore Depth API for Unityのdepthデータ値の表現とRGB565テクスチャへのマッピングは下記となっています。 f:id:peugeot-106-s16:20200628222732p:plain

RGB565テクスチャのピクセルはr, g, bのフィールドからそれぞれ範囲(0.0-1.0)の数値として取得できるので、整数値に変換し、ビットシフトして合計すれば元の16bit値を復元することができます。

抜粋するとこんな感じです。

#define ARCORE_MAX_DEPTH_MM 8191.0
#define ARCORE_FLOAT_TO_5BITS 31        // (0.0, 1.0) -> (0, 31)
#define ARCORE_FLOAT_TO_6BITS 63        // (0.0, 1.0) -> (0, 63)
#define ARCORE_RGB565_RED_SHIFT 2048    // left shift 11 bits
#define ARCORE_RGB565_GREEN_SHIFT 32    // left shift 5 bits

float depth = (pixel.r * ARCORE_FLOAT_TO_5BITS * ARCORE_RGB565_RED_SHIFT)
    + (pixel.g * ARCORE_FLOAT_TO_6BITS * ARCORE_RGB565_GREEN_SHIFT)
    + (pixel.b * ARCORE_FLOAT_TO_5BITS);
depth = min(depth, ARCORE_MAX_DEPTH_MM);
  • Rを0-31範囲に変換し、11bitシフトする
  • Gを0-64範囲に変換し、5bitシフトする
  • Bを0-31範囲に変換する
  • 3つの数値を足し合わせる
  • ARCore Depth API では8メートルまでの距離を扱うことができるので8191を超えた値は8191に収める

とすることで、depth値を求めることができます。

なお、実はこの処理はARCoreDepth.cginc 内で ArCoreDepth_GetMeters() として用意されているので、普通はこちらを使えばOKです(笑)

https://github.com/google-ar/arcore-unity-sdk/blob/fbc21676ef0bb305c775fa3be593b8c46a62c211/Assets/GoogleARCore/SDK/Materials/ARCoreDepth.cginc#L45-L55

まとめ

ARCore Depth API for Unity で取得できるdepthフォーマットとShader内でのデータ変換について書きました。

しかし、このぼんやりとしたdepthはどうにかならないんでしょうか。。w (フィルタ無しのRAWデータ触りたい)

MRTK v2.4.0 ボタンクリックイベントの送信元を取得する (Update)

しばらくアウトプットする暇も取れない感じでしたが久しぶりの投稿(小ネタ)です!

MRTKのUIコンポーネントを利用し、複数ボタンのOnClickイベントを同一のイベントハンドラにリンクさせました。
この時OnClickイベントはイベント送信元の情報をパラメータで渡すようにはなっていないため、どのボタンからイベントが送信されたか判断できず困ってしまいました。
ボタンごとにイベントハンドラを書くなんて嫌ですよね笑!

解決した方法をサンプルプロジェクトとしてシェアします。
(もっとスマートな方法が用意されているんじゃないのかなぁ。。)

2020/06/07 Update

すぐにホロラボの @deco_c_ 君がヒントをくれました! 確かにこの手があったか~。シンプルで素敵。

こちらのケースもサンプルプロジェクトに追加しました。

問題

OnClick()イベントはパラメータを持たないので送信元の情報などは一切通知されません。
(senderが欲しいだけだったのですがかなり調べることにww)

f:id:peugeot-106-s16:20200606225317p:plain https://github.com/microsoft/MixedRealityToolkit-Unity/blob/72ea542dff0ab5cbd229d9c93a4f138f37172cc5/Assets/MRTK/SDK/Features/UX/Scripts/Buttons/ButtonConfigHelper.cs#L87-L99

case01: Post detect

サンプルプロジェクトの構成

f:id:peugeot-106-s16:20200606223602p:plain
Multi buttons click event are linked to an event handler

プロジェクト一式
github.com

解決策

イベントハンドラ内でFocusProviderよりフォーカスが当たっているInteractableを取得する。
そこにたどり着くまでに色々とステップがある(下記 GetEventSender() メソッド参照)

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.UI;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public ToolTip toolTip;

    public void ButtonClicked()
    {
        this.toolTip.ToolTipText = this.GetEventSender()?.name;
    }

    private GameObject GetEventSender()
    {
        foreach (var inputSource in CoreServices.InputSystem.DetectedInputSources)
        {
            foreach (var pointer in inputSource.Pointers)
            {
                if (pointer.IsInteractionEnabled)
                {
                    var sender = CoreServices.InputSystem.FocusProvider.GetFocusedObject(pointer);
                    if (sender?.GetComponent<Interactable>() != null)
                    {
                        return sender;
                    }
                }
            }
        }
        return null;
    }
}

https://github.com/satoshi-maemoto/MRTKButtonSenderSample/blob/master/Assets/Scripts/NewBehaviourScript.cs

こんな感じに動きます。


MRTK Button Sender Sample

case02: Event wrap

サンプルプロジェクトの構成

f:id:peugeot-106-s16:20200607110420p:plain
OnClick extender are attached to each buttons

解決策

各ボタンにアタッチした ClickEventRouter コンポーネントが、ButtonConfigHelper.OnClickイベントをフックして自オブジェクトをパラメータに追加したかたちでInvoke()する。

using Microsoft.MixedReality.Toolkit.UI;
using System;
using UnityEngine;
using UnityEngine.Events;

public class ClickEventRouter : MonoBehaviour
{
    [Serializable]
    public class InternalButtonClickedEvent : UnityEvent<GameObject> { }

    public InternalButtonClickedEvent OnClick;

    void Start()
    {
        this.GetComponent<ButtonConfigHelper>()?.OnClick.AddListener(() =>
        {
            this.OnClick?.Invoke(this.gameObject);
        });
    }
}

https://github.com/satoshi-maemoto/MRTKButtonSenderSample/blob/master/Assets/Scripts/ClickEventRouter.cs

こんな感じにcase01と同様に動きます。


MRTK Button Sender Sample take2

まとめ

複数UIコンポーネントイベントハンドラを共有するという構成はコードをシンプルにするためによく使われるものだと思うのでSDKにきっと機能が用意されてているに違いないと探しまくったのですがどうやら無さそうなのでひと手間必要そうです(笑)

UnityネイティブプラグインをVisual Studio でステップ実行デバッグする

Unityのネイティブプラグインを開発していると、Unityで実行中にプラグイン内部のコードや変数の状態をデバッグしたくなる時がよくありますよね。
これは簡単な手順で行うことができるので手順を書いておきます。

デバッグ実行できる条件

つまり自作プラグインオープンソースのものであればデバッグ可能です。
反対に .dllファイルだけ提供されているようなプラグインデバッグできないということです。

デバッグ実行するための手順

ネイティブプラグインをDebugビルドする

Debug構成でプラグインをビルドします。 f:id:peugeot-106-s16:20190730153551p:plain

ビルドが成功すると .dllファイルと .pdbファイルが出力されます。 f:id:peugeot-106-s16:20190730153721p:plain

Unityのプラグインフォルダに必要なファイルを配置する

.dllファイルと .pdbファイルをUnity内のPluginsフォルダに配置します。 f:id:peugeot-106-s16:20190730153944p:plain

Visual StidoからUnityにアタッチする

Unityプロジェクトを開いたのち、プラグインプロジェクトを開いているVisual Studio の [デバッグ]-[プロセスにアタッチ]メニューを選択します。
"プロセスにアタッチ"ダイアログが表示されるのでUnityのプロセスを選択して[アタッチ]します。
プロセスはたくさん列挙されますが"unity"で絞り込めば見つけやすいです。 f:id:peugeot-106-s16:20190730154349p:plain

ブレークポイントの設定を行う

ステップ実行したい部分にブレークポイントを設定しましょう。

UnityでPlayする

この状態でUnityでPlayすると、なんということでしょう。
プラグイン側のソースコード上のブレークポイントが効きましたね。
もちろん、普通にステップ実行ができますし、変数の内容もウォッチすることができます。 f:id:peugeot-106-s16:20190730155029p:plain

ブレークポイントが効かない時は

プラグインプロジェクト側とUnity側で .dllファイルと .pdbファイルのバージョンが一致していないことが原因として考えられます。
再ビルドとUnityへのファイル更新を再度行ったり、Unityの再起動を行ってみてください。

まとめ

これでプラグインを絡めてUnityアプリケーションを開発するスピードが爆上がりですね(笑)!