- 投稿日:2020-09-13T20:18:33+09:00
UnityでMobile-FFmpegを使う
はじめに
Unityで開発中のアプリ内で動画ファイル・音声ファイルを加工する処理が必要になり、Mobile-FFmpegを利用することになった。導入して使えるようになるまでにやったことのメモ。
Mobile-FFmpeg とは
FFmpegのAndroid/iOS対応版オープンソースライブラリ。内容はFFmpegおよびFFprobeの実装と外部ライブラリからなる。アップデートも活発に行われている。コマンドライン版FFmpegと全く同じ感覚でオプションコマンド文字列を指定して呼び出すシンプルなAPIになっているのが特徴。
https://github.com/tanersener/mobile-ffmpeg注意
MobileFFmpegは40以上の外部依存ライブラリがあるため、まずMobileFFmpegをアプリのどこに使うのか予め使用目的を定め、それに必要な機能と必須のライブラリを明確にしておくと良いでしょう。
この記事はMobile-FFmpeg Version 4.4、Unity2018以降を対象に書かれています。
ビルド済みバイナリ
自前でビルドできますがビルド済みバイナリも用意されているので今回はこれを使いました。
バイナリは内包する外部ライブラリの違いで8種類のバリエーションが用意されています。Packages
min
: 最小限のパッケージmin-gpl
: minにGPLライセンスライブラリを追加したものhttps
: TLS関係のライブラリを追加したものhttps-gpl
: httpsにGPLライセンスライブラリを追加したものaudio
: オーディオ関係のライブラリを追加したものvideo
: ビデオ関係のライブラリを追加したものfull
: フルセット(GPLライセンスを含まない)full-gpl
: フルセット(GPLライセンスを含む)用途に応じて上記の中からpackageを選択し、ファイルをダウンロード
Android用のライブラリはaarファイルをダウンロード
・(ファイル名) :mobile-ffmpeg-[Package]-[Version].aar
iOS用のライブラリはxcframework.zipファイルをダウンロードして解凍
・(ファイル名) :mobile-ffmpeg-[Package]-[Version]-ios-xcframework.zip
パッケージごとの詳細は以下を確認。
2.1 PackagesUnityへのインポート
ダウンロードした
.aarファイルを
/Assets/Plugins/Android
zipを解凍してframeworkファイル群を
/Assets/Plugins/iOS
に配置。Unity側のC#から呼び出す
AndroidとiOSそれぞれ、Pluginモジュール呼び出し用ラッパーを実装します。基本的にはExecuteとCancelのAPI2つ呼び出せればOKです。
MobileFFmpegのGitHub、isshue#258のスレッドを参考にさせていただきました。
https://github.com/tanersener/mobile-ffmpeg/issues/258#issuecomment-663913978コード
ソースファイルは3つあります。.mmファイルは
Assets/Plugins/iOS
に置いてください。FFmpegWrapper.csusing UnityEngine; public class FFmpegWrapper { private static int Execute(string command) { #if UNITY_ANDROID using (AndroidJavaClass configClass = new AndroidJavaClass("com.arthenica.mobileffmpeg.Config")) { AndroidJavaObject paramVal = new AndroidJavaClass("com.arthenica.mobileffmpeg.Signal").GetStatic<AndroidJavaObject>("SIGXCPU"); configClass.CallStatic("ignoreSignal", new object[] { paramVal }); using (AndroidJavaClass ffmpeg = new AndroidJavaClass("com.arthenica.mobileffmpeg.FFmpeg")) { int code = ffmpeg.CallStatic<int>("execute", new object[] { command }); return code; } } #elif UNITY_IOS return MobileFFmpegIOS.Execute(command); #else return 0; #endif } private static int Cancel() { #if UNITY_ANDROID using (AndroidJavaClass configClass = new AndroidJavaClass("com.arthenica.mobileffmpeg.Config")) { using (AndroidJavaClass ffmpeg = new AndroidJavaClass("com.arthenica.mobileffmpeg.FFmpeg")) { int code = ffmpeg.CallStatic<int>("cancel"); return code; } } #elif UNITY_IOS return MobileFFmpegIOS.Cancel(); #else return 0; #endif }MobileFFmpegIOS.csusing UnityEngine; using System.Collections; using System.Runtime.InteropServices; public class MobileFFmpegIOS : MonoBehaviour { #if UNITY_IOS [DllImport("__Internal")] private static extern int _execute(string command); [DllImport("__Internal")] private static extern void _cancel(); #endif /** * Synchronously executes FFmpeg command provided. Space character is used to split command * into arguments. * * @param command FFmpeg command * @return zero on successful execution, 255 on user cancel and non-zero on error */ public static int Execute(string command) { int result = -1; #if UNITY_IOS if (Application.platform == RuntimePlatform.IPhonePlayer) { result = _execute(command); } #endif return result; } /** * Cancels an ongoing operation. * * This function does not wait for termination to complete and returns immediately. */ public static void Cancel() { #if UNITY_IOS if (Application.platform == RuntimePlatform.IPhonePlayer) { _cancel(); } #endif } }以下は
Assets/Plugins/iOS
に配置MobileFFmpeg.mm//In unity, You'd place this file in your "Assets>plugins>ios" folder //Objective-C Code #import <mobileffmpeg/MobileFFmpeg.h> extern "C" { /** * Synchronously executes FFmpeg command provided. Space character is used to split command * into arguments. * * @param command FFmpeg command * @return zero on successful execution, 255 on user cancel and non-zero on error */ int _execute(const char* command) { return [MobileFFmpeg execute: @(command)]; } /** * Cancels an ongoing operation. * * This function does not wait for termination to complete and returns immediately. */ void _cancel() { [MobileFFmpeg cancel]; } }Unityから呼び出す
Executeのパラメータにオプションコマンドを文字列で渡し呼び出します("ffmpeg "は不要なので注意)。成功すれば0が返ります。
Exampleprivate void Test() { var input = Application.persistentDataPath + "/input.mov"; var output = Application.persistentDataPath + "/output.mp4"; int rc = FFmpegWrapper.Execute(string.Format("-i {0} {1}", input, output)); Debug.Log("Return Code is " + rc); }非同期実行の
FFmpeg.executeAsync
関数も用意されていますが試してません。申し訳ございません。。。その他
○Xcodeでリンカーエラーが出たら
[Link Binary with Libraries]に以下のライブラリがなければ追加する。
bzip2, iconv, libuuid, zlib, AudioToolbox, VideoToolbox, AVFoundation
(libz.tbd, libbz2.tbd, libiconv.tbd)Armv7に対応してないっぽいのでArchitecturesはArm64専用でビルドしました
○実行時エラー
・ファイル書き込み権限がない、Outputファイルと同名のファイルがすでに存在する場合失敗します
・コマンドに応じて必要なライブラリが決まるので、モジュールがない場合は失敗します参考記事
AndroidでFFmpegを使って音声ファイルを解析・変換する
https://qiita.com/tarumzu/items/a4d15957a144f520f842
それFFmpegで出来るよ!
https://qiita.com/cha84rakanal/items/e84fe4eb6fbe2ae13fd8
FFmpegで動画をGIFに変換
https://qiita.com/wMETAw/items/fdb754022aec1da88e6e
- 投稿日:2020-09-13T18:29:57+09:00
動いているLive2Dに直接ペイントする。(Unity)
サンプルコード
Github上にサンプルコードを用意しておきました。
ColorPickerTriangleのアセットとLive2DSDKを自前でインポートする必要があります。
https://github.com/Ganeesya/Live2DLivePaintingSampleCubism Frameworkで拡張できる領域
Live2Dの描画は頂点計算だけは独自で行われるものの、描画処理システムは完全に3Dのシステムで動いています。
上の図はCubism Frameworkを使用した際の大まかなデータの流れを示しますが、
うす緑色のCubismFrameworkの部分とそこに含まれるCubismRendererのコードは公開されていて、
知識があればこの部分は改造することが可能です。今回はUnityにおいてメッシュUVポイントを求めるのにCubismFramework側を、
ペイント結果を反映させるのにCubismRendererを拡張しています。今回のテクスチャ周りの仕組み
今回のサンプルでは4種類のテクスチャを使用します。
- ベースのCubismから出力されるテクスチャ。MainTextureとして使用
- ペイントできる領域を示すためのPaintArea
- ペイント結果を保持しておくRenderTexture
- ペイント結果でも塗りつぶせない主線などを上書きする情報をもつOverline
2のPaintAreaは3のペイントを行うときにだけ使用します。
残りの最終的な描画結果に関しては1,3,4をCubism Framework for Unityに含まれる標準Shaderを改造したShaderで合成して使用しています。fixed4 frag (v2f IN) : SV_Target { fixed4 OUT = tex2D(_MainTex, IN.texcoord) * IN.color; //RenderTexture fixed4 RET = tex2D(_Retatch, IN.texcoord) * IN.color; fixed4 Over = tex2D(_OverLine, IN.texcoord) * IN.color; OUT.rgb = OUT.rgb * ( 1.0 - RET.a ) + RET.rgb; OUT.rgb = OUT.rgb * ( 1.0 - Over.a ) + Over.rgb; // Apply Cubism alpha to color. if( isPremultiedAlphaOut == 1 ){ CUBISM_APPLY_ALPHA(IN, OUT); } return OUT; }MainTextureをベースにPaint結果をまず上書き、そのあと主線用のTextureを上書きする流れです。
RenderTextureへの描画処理
専用のレイヤーを作成してカメラとペン用のPanelの実態を作成して、
操作することで描画を行なっています。
初期ではPenのTransformを動かして描画していましたがこの方法ではメッシュが重なった箇所で描画するときに不便なので、
カメラへリンクさせたCommandBufferを使用した方式に変更して描画しています。
カメラのClearステップを解除しないと描画が消えてしまうことに注意。クリック地点からUV空間座標を求める
参考リンク
https://qiita.com/Es_Program/items/fe9c66afc4cbddc7fdfd
https://esprog.hatenablog.com/entry/2016/05/08/165445三角形上の点における面積比重の割合でPositionとUVを変換する考え方です。
入力が便宜上3次元になっていますがLive2Dのローカル空間を取り扱うのでZは無視されます。
Unity上でのLive2DMeshへのアクセスも難しい感じなのでUnityのMeshに変換されたあとにアクセスしています。
NativeFrameworkであればUserModel経由でDrawableの情報に直接アクセスしたほうが良いかもしれません。private bool RayCast(Vector3 inputPosition, Mesh mesh, out Vector2 resultUV) { // アクセス毎にメモリコピーするので出しておく。 var vertices = mesh.vertices; var indices = mesh.triangles; var uvs = mesh.uv; for (int i = 0; i < indices.Length; i += 3) { var vertexPositionA = vertices[indices[i]]; var vertexPositionB = vertices[indices[i + 1]]; var vertexPositionC = vertices[indices[i + 2]]; var crossProduct1 = (vertexPositionB.x - vertexPositionA.x) * (inputPosition.y - vertexPositionB.y) - (vertexPositionB.y - vertexPositionA.y) * (inputPosition.x - vertexPositionB.x); var crossProduct2 = (vertexPositionC.x - vertexPositionB.x) * (inputPosition.y - vertexPositionC.y) - (vertexPositionC.y - vertexPositionB.y) * (inputPosition.x - vertexPositionC.x); var crossProduct3 = (vertexPositionA.x - vertexPositionC.x) * (inputPosition.y - vertexPositionA.y) - (vertexPositionA.y - vertexPositionC.y) * (inputPosition.x - vertexPositionA.x); if ((crossProduct1 > 0 && crossProduct2 > 0 && crossProduct3 > 0) || (crossProduct1 < 0 && crossProduct2 < 0 && crossProduct3 < 0)) { var vAB = vertexPositionB - vertexPositionA; var vAC = vertexPositionC - vertexPositionA; var weightAll = ( vAB.x * vAC.y - vAB.y * vAC.x ) / 2f; var vAP = inputPosition - vertexPositionA; var weightC = ( vAB.x * vAP.y - vAB.y * vAP.x ) / 2f; var vBC = vertexPositionC - vertexPositionB; var vBP = inputPosition - vertexPositionB; var weightA = ( vBC.x * vBP.y - vBC.y * vBP.x ) / 2f; var weightB = weightAll - weightA - weightC; var uvA = uvs[indices[i + 0]]; var uvB = uvs[indices[i + 1]]; var uvC = uvs[indices[i + 2]]; weightA /= weightAll; weightB /= weightAll; weightC /= weightAll; resultUV = uvA * weightA + uvB * weightB + uvC * weightC; return true; } } resultUV = new Vector2(); return false; }今回はこれで手に入ったUV空間上にPenのTransfromを移動させてから転写コマンドを行なっていますが、
この方法ではペンの回転に対応していなかったりMeshのひしゃげ具合に関係なく描画されるケースがあるので、
Mesh自体の変換で行うともっときれいにできそうです。
(今回は実証のみだったのでやらない。Live2Dの部品へ必要な処理を自動で行う
ここまでは原理面を説明してきましたが実運用面でみたときにUnity上でモデルをインポートしたあとに、
各Drawableやモデルにコンポーネントを貼り付けていく必要があります。
人力でこれをやるとDrawableへの操作があるため100枚くらいのArtMeshがあると地獄の作業になります。
これを回避する機能がCubismImporterです。
CubismFramework for Unityに含まれている機能で、概要としては
EditorフォルダにStatic関数を作成してInitializeOnLoadMethodでCubismImporter.OnDidImportModelへ関数を登録するとUnityへLive2Dモデルが読み込まれた際に登録された処理が動作してくれる仕組みです。internal static class PaintingCustomImporter { [InitializeOnLoadMethod] private static void RegisterModelImporter() { CubismImporter.OnDidImportModel += OnModelImport; } private static void OnModelImport(CubismModel3JsonImporter sender, CubismModel model) { // drawableのテクスチャ番号からマテリアルの入れ替えとRaycastableの付与を行う。 var drawables = model.Drawables; foreach (var drawable in drawables) { if( drawable.TextureIndex != 0 ) continue; var renderer = drawable.GetComponent<CubismRenderer>(); switch (renderer.Material.name.Replace(" (Instance)","")) { case "Unlit" : renderer.Material = Resources.Load<Material>("compositMats/UnlitRp"); break; case "UnlitAdditiveMasked" : renderer.Material = Resources.Load<Material>("compositMats/UnlitAdditiveMaskedRp"); break; case "UnlitAdditive" : renderer.Material = Resources.Load<Material>("compositMats/UnlitAdditiveRp"); break; case "UnlitMasked" : renderer.Material = Resources.Load<Material>("compositMats/UnlitMaskedRp"); break; case "UnlitMultiplyMasked" : renderer.Material = Resources.Load<Material>("compositMats/UnlitMultiplyMaskedRp"); break; case "UnlitMultiply" : renderer.Material = Resources.Load<Material>("compositMats/UnlitMultiplyRp"); break; default: Debug.LogWarning("unknown mat Type!!"); break; } var rayCastable = drawable.GetOrAddComponent(typeof(CubismRaycastable)) as CubismRaycastable; if (rayCastable != null) { rayCastable.Precision = CubismRaycastablePrecision.Triangles; } } // 不要なAnimatiorを削除 var animator = model.GetComponent<Animator>(); if (animator != null) { Object.DestroyImmediate(animator); } // modelに対して必要なComponentを付与 model.GetOrAddComponent(typeof(Animation)); model.GetOrAddComponent(typeof(SlidableMotionController)); model.GetOrAddComponent(typeof(CubismUpdateController)); model.GetOrAddComponent(typeof(PaintController)); model.GetOrAddComponent(typeof(CubismRaycaster)); } }今回つくったペイント用のImporterでは以下の作業が自動的に行われます。
- Texture番号が0のDrawableのマテリアルをペイントに対応したものに切り替える
- 当たり判定用のComponentをDrawableに付与
- 標準でModelへとりつけられるAnimatiorがスライダー再生が不可能なのでAnimationへ振り替え直す。
- その他ペイントに必要なComponentをModelへ付与する。
このCubismImporterを利用すれば大量にモデルを組み込んで特殊な描画を行うときも労力を少なく処置できるはずです。
- 投稿日:2020-09-13T17:59:41+09:00
Luaメモリ詳細診断
現在、Luaはすでに各開発チームがプロジェクトのHot Fix機能を実現するための一般的な実装方法だと思われますが、プロジェクトに機能追加、バッグ修正は非常に柔軟で便利です。しかし、プロジェクトで大量にLuaを使用することにより、プロジェクト実行時の重要なパーフォマンス問題の一つでもあります。特にメモリ関連の性能問題は、メモリの割当過ぎかメモリリークかどちらも開発中のプロジェクトで集中的に発生しているようです。
このため、UWA GOTでLuaのメモリ詳細診断機能をリリースしました。すでにUWA GOT Onlineサービスを購入済みのユーザー様は、実機でテストしたデータをUWAホームページ にアップロードし、すぐLuaのメモリテストレポートを確認することができます。
現在のバージョンはすでに主流のuLua、ToLua、SLua とxLuaをサポートしております。開発チームがLuaのNativeライブラリをカスタマイズし、リネームすれば、UWAEngine.SetOverrideLuaLibを使用してライブラリ名を指定することができます。Luaメモリ診断は全体ヒップメモリ、ヒップメモリの詳細割当てとMonoオブジェクト参照の三つの部分に分かれております。以下をご参照ください。
1、全体ヒップメモリ
本テストレポートではLuaの全体メモリトレンド及びLuaのいくつ重要なオブジェクトCountを表示しております。Table,UserdataとFunction(通常threadのCountが少なく且つ安定的であるあるため、ここでは表示されません)。
上図の通り、プロジェクトが実行されたフレームごとのLuaメモリの使用状況をはっきり確認することができ、それぞれの数値が当初の予定値よりずれているかどうか、またはメモリリークのリスクがあるかどうか検討できます。2、ヒープメモリ詳細割当
本レポートでは開発者がLuaヒープメモリの割当の多い関数を特定するのによく使用されます。開発者のLuaヒップメモリ使用の最適化のため、これらの関数の任意の1フレームの割り当てられたスタック情報を確認でき、Lua GCの発生率や発生時のメモリコストの低減につながります。
備考:Lua関数のネーミング方法はX@Y:Z,内Xが関数名,取得出来ない場合はXがデフォルトでunknownに変わります;Yは当該関数が定義したファイルの位置;Zは当該関数が定義された終了の行番号。特に注意して頂きたいのは、LuaのスクリプトをBytecodeで実行される場合は,数値はずっと0になるため、テスト時はできるだけ、Luaのソースコードでの実行を推奨いたします。
また、Monoメモリの逆順分析機能と同じように、Luaのメモリ情報でも同様に確認することができます。閲覧方法を切り替えるだけで、どのLuaのスクリプトのどの行のコードで、大量のメモリを使用しているか見つかることができます。そして、開発者が直接対応されたLuaスクリプトを開き、その行のコードを修正でき、性能改善がこのように簡単にできます。
3、Monoオブジェクト参照
すべてのLuaプラグインでは,以下のような類似仕組みが存在しますが
C#レイヤーにCacheを保持し、LuaにアクセスされたC#レイヤーのオブジェクトを参照します。この仕組みは主にLuaが再度C#オブジェクトにアクセスする際に、当該オブジェクトはすでにGCに回収され、ロジックエラーにつながることを防ぐためです。そのため、Lua内にあるC#レイヤーのオブジェクト参照用にCacheを保持すると、それを解放されず、このような参照が多くなればなるほど、C#レイヤーのメモリリークに繋がります。
開発者がこのような状況を調査しやすくするため、Monoオブジェクト参照のレポートでは、上述のCache内のC#レイヤーのオブジェクトを整理し、Cacheで現れたオブジェクトのタイプや各タイプのオブジェクトの合計数量を統計しました。当該オブジェクトはUnityEngine.Objectに継承され、当該オブジェクトタイプでDestroyされたオブジェクト数量も統計されます。詳細下図参照:
上図のそれぞれのオブジェクトタイプをクリックすれば、合計数量の変化を確認できます。ある数量を持続的に上昇しているタイプに対して、二つのサンプリングポイントの違うオブジェクト参照を比較すれば、さらにLua内の合理的ではない参照を特定することができます。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
- 投稿日:2020-09-13T11:51:29+09:00
Hololends2セットアップから実機にビルドするまでの知見
前知識0でHolo1も触っていない私が環境構築から実機で動かすまでに得た知見をまとめたものです。
使用するUnityのバージョン
2019のLTSを推奨していました。本環境は2019.4.10f1を仕様しています。
こちらから最新の2019LTSをDL推奨。
https://unity3d.com/unity/qa/lts-releasesUnityProjectの設定
こちらが参考になりました。
https://docs.microsoft.com/ja-jp/windows/mixed-reality/recommended-settings-for-unityUWPにSwitchPlatformしたところ警告が出ていますね。
VisualStudioの環境構築が足りていないと警告が出るようです。
その場合の対応はこちらが参考になりました。
https://qiita.com/chomado/items/38baafdfd96892d3b41dMRTKもインストールしました。VRを開発した方ならVRTKと言えば察しがつくかと思います。入力系の処理がいい感じにラップしてある便利ツールだと思っていただければよいかと思います。Unity2019をインストールしたのはMRTKのサポートが保証されているからです。
https://github.com/microsoft/MixedRealityToolkit-Unity/releasesMicrosoft.MixedReality.Toolkit.Unity.Foundation.X.X.X.unitypackage
をDLしましょう。(X.X.X はバージョン)WiondowsとHololoends2の設定
MRTKを導入した状態でビルドしたらエラーがでるんですけど。。
Assets\MixedRealityToolkit.Providers\WindowsMixedReality\WindowsMixedRealityArticulatedHand.cs(418,73): error CS0246: The type or namespace name 'HandJointKind' could not be found (are you missing a using directive or an assembly reference?) Assets\MixedRealityToolkit.Providers\WindowsMixedReality\WindowsMixedRealityArticulatedHand.cs(111,17): error CS0246: The type or namespace name 'HandMeshObserver' could not be found (are you missing a using directive or an assembly reference?) Assets\MixedRealityToolkit.Providers\WindowsMixedReality\WindowsMixedRealityArticulatedHand.cs(382,33): error CS0246: The type or namespace name 'HandJointKind' could not be found (are you missing a using directive or an assembly reference?) Assets\MixedRealityToolkit.Providers\WindowsMixedReality\WindowsMixedRealityArticulatedHand.cs(412,26): error CS0246: The type or namespace name 'JointPose' could not be found (are you missing a using directive or an assembly reference?) I am running on Windows 10 Insider preview with OS build : 18362.30 Version : 1903 Also I double checked with build setting Minimum SDK is 10.0.10240.0 Target SDK version is “Latest installed”かなりハマりました。
エラーコードでググると下のページがみつかりましたが、エラーがとれませんでした。
https://stackoverflow.com/questions/55706007/facing-issue-when-i-build-the-mixedrealitytoolkit-unity-v2-sample-project原因は、Windows10SDKの未インストールでした。
https://developer.microsoft.com/ja-jp/windows/downloads/windows-10-sdk/
インストーラーからSDKをDLしたのちUnityEditorを再起動したところビルドできました。
人にとっては、Libraryフォルダを削除したら直ったりBuildSettingsのMinimumPlatformVersionを10.0.10240.0に選択すえうと直ったりと色々な原因がありそうです。(自分の環境で最適化した)Unityの設定
MRTK導入エラーで設定をいじくりまわした結果、複数のサイトから適用した形になりましたのでスクリーンショットを共有します。(あくまで私の環境でのベストですので)
BuildSettings->PlayerSettings->XRSettings
EnableDepthBufferShariungをオフにする情報はMS公式のYoutube動画で言っていたので正しいんじゃないですかね(他のサイトだとONにしていたりするのでちょっと不安)
https://www.youtube.com/watch?v=q1cvbJP2miM&frags=wn&ab_channel=%E6%97%A5%E6%9C%AC%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%BD%E3%83%95%E3%83%88%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE%E5%85%AC%E5%BC%8F%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%ABPublishingSettingsのCapabilitiesのチェック入れ忘れも気をつけてください。SpatialPerceptionとかね。
Edit->ProjectSettings->Quality
QualityLevelをVeryLowに
上部のDefaultテキストの右部にある▼マークから変更です。Window->Rendering->Lighting
RealLightingはオフにしたほうがいいらしいです(自分の環境で最適化した)VisualStudioInstaller
VisualStudioのバージョンは2019(2017だとダメでした)
・C++デスクトップ開発
・ユニバーサルWIndwosプラットフォーム開発
・Unityによるゲーム開発
個別のコンポーネントからUSBデバイスの接続を選択しておくと有線接続で実機に転送できるようになります。(自分の環境で最適化した)VisualStudio
前提としてUnityでビルドして吐き出されたプロジェクト名.sinを開いておいてください。
プロジェクト名(UniversalWindows)を右クリック
・スタートアッププロジェクトに設定
・プロパティ->デバッグ->コンピューター名にHololends2がアクセスしているIPアドレスを入力(入力すると無線接続でビルドができます)
詳しくはこちら
https://medium.com/kadinche-engineering/hololens-2-%E3%81%AE%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E3%82%92%E3%81%97%E3%81%A6%E5%AE%9F%E6%A9%9F%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%99-7a15bfe5e088