- 投稿日:2020-01-22T22:33:43+09:00
【UnityEditor拡張】毎フレームRepaintされるPropertyDrawer
TL;DR
Unityのユーザ定義PropertyDrawerの描画を毎フレーム自動更新する方法。
EditorWindowを継承していればUpdateがあるし、Editorを継承していればRequiresConstantRepaintがあります。
でもPropertyDrawerにはなくて不便なので頑張ってどうにかしました。記事の下の方に基底クラスとして汎用化したものを置いてあります。
一部Reflectionを使用、Unity2019.3.0f3にて動作確認済。
モチベーション
タイムラインと再生機能つきのPropertyDrawerを作りたかったのです。
現在位置を表示したいわけですが、マウスを動かしたりクリックしたりしないとRepaintが呼ばれないので、常に適切に表示するには別途Inspectorを拡張してoverride RequiresConstantRepaint() => true;
する必要があります。
何もしなくても、あるいはAttributeをつけるだけで手軽に拡張できるのがPropertyDrawerの良さだというのに、これではあんまりです。実現までの道のり
過程はいいからモノを出せという方は飛ばしてどうぞ。
PropertyDrawer内部から自身をRepaintする
PropertyDrawerそのものにはRepaintという概念がないので、Repaintするためには親であるEditorのRepaintを呼ぶ必要があります。
アクティブなEditorはActiveEditorTracker.sharedTracker.activeEditors
でアクセスできるので、以下のように定義してRepaint()
すればよいですね。SerializedObject parentSerializedObject; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { parentSerializedObject = property.serializedObject; /*EditorGUI.BeginProperty(position, label, prop); ... (いつもの部分。以降のコードでは省略されます) EditorGUI.EndProperty();*/ } void Repaint() { foreach (var editor in ActiveEditorTracker.sharedTracker.activeEditors) { if (editor.serializedObject == parentSerializedObject) { editor.Repaint(); return; } } }ではこれをどこから呼ぶかですが、EditorWindowと違ってPropertyDrawerにはUpdateがありません。
EditorApplication.update
そこで出てくるのが、EditorのUpdateをフックするためのこのevent。
EditorApplication.update += Repaint;
とすれば、毎UpdateごとにRepaint()
が実行されるようになります。これを使っていきましょう。eventのadd/removeは大抵の場合OnEnabled/OnDisabled的な部分に書きますが、PropertyDrawerにはその類の「最初と最後に一度だけ呼ばれる」イベント、virtualメソッドが存在しません。
仕方がないのでOnGUI内に書きます。OnGUIは何度も呼ばれるので、addの前にremoveするのを忘れずに。SerializedObject parentSerializedObject; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { parentSerializedObject = property.serializedObject; EditorApplication.update -= Repaint; //増殖を防ぐ EditorApplication.update += Repaint; } void Repaint(){/*略*/}これで、とりあえず毎フレームRepaintはされるようになりました。
event購読を解除する
addの前にremoveを挟むことで同一PropertyDrawer内での増殖は防いでいますが、開き直したPropertyDrawerは別のインスタンスになるようで、このままでは「選択しているGameObjectを変えて再び元のGameObjectを選択し直す」を繰り返すことでリークします。
これを防ぐため、選択項目が変わったらRepaintをremoveするようにしましょう。
Selection.selectionChanged
を使う選択中のObjectの変化をフックするためのeventです。
SerializedObject parentSerializedObject; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { parentSerializedObject = property.serializedObject; EditorApplication.update -= Repaint; EditorApplication.update += Repaint; Selection.selectionChanged -= OnSelectionChanged; Selection.selectionChanged += OnSelectionChanged; } void Repaint(){/*略*/} void OnSelectionChanged() { if (parentSerializedObject == null || parentSerializedObject.targetObject != Selection.activeObject) { EditorApplication.update -= Repaint; Selection.selectionChanged -= OnSelectionChanged; } }これで大丈夫な気がしますね。nullチェックもバッチリです。
早速PropertyDrawerを表示した状態で、別のGameObjectを選択してみましょう。
ダメみたいですね……
_unity_self
なるものがnullだそうです。知らんがな。VisualStudioでエラー箇所を確認してみると、
なんとparentSerializedObject
にまだ実体があり、しかしそのプロパティにアクセスできない状態。
targetObject
のget内でエラーが出てるみたいですね。
これは……Unityのバグかなあ。気が向いたらバグレポートでも出しますかね。
m_NativeObjectPtr
で判断上のスクショを見ると、parentSerializedObjectのうち、ただ一つだけ正常にアクセスできているメンバがあります。
m_NativeObjectPtr
、型はSystem.IntPtr
。publicでないフィールド。選択解除時の値は0。
targetObjectのアドレスを保有するための内部フィールドだと考えられますね。
この値が0ならnullだと見なせばよさそうです。Reflectionしましょう。また、同スクリプトを載せた他のObjectを選択するときや選択解除するときも同様に
m_NativeObjectPtr
は0だったので、選択中オブジェクトの比較は不要そうです。
nullチェックもいらなさそうですが、ちょっと怖いのでこっちは一応入れておきます。SerializedObject parentSerializedObject; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label){/*略*/} void Repaint(){/*略*/} //キャッシュ static readonly FieldInfo fi_m_NativeObjectPtr = typeof(SerializedObject) .GetField("m_NativeObjectPtr", BindingFlags.NonPublic | BindingFlags.Instance); void OnSelectionChanged() { if (parentSerializedObject == null || (IntPtr)fi_m_NativeObjectPtr.GetValue(parentSerializedObject) == IntPtr.Zero) { EditorApplication.update -= Repaint; Selection.selectionChanged -= OnSelectionChanged; } }これでエラーは出なくなりました。
本当にリークしていないかどうかは、OnGUI()
あたりにDebug.Log($"UpdateEvent Count = {EditorApplication.update.GetInvocationList().Length}");とでも書いておけばConsoleで確認できます。いくら選択し直しても数字が増えていかなければOK。
できたもの
コンパイルやUndo/Redoのフック、Updateフレームレートの変更、その他諸々追加して基底クラス化したものがこちら。
カスタムインスペクタ上で表示する場合はカスタムインスペクタ側でRequiresConstantRepaintするはずなので、二重にRepaintが走らないようフィルタリングしています。常識的な範囲でご自由に使ってどうぞ。リーク確認漏れとかバグとかあったらぜひ教えてくださいませ。
ConstantRepaintPropertyDrawer.csusing UnityEditor; using UnityEngine; using System; using System.Reflection; using UnityEditor.Compilation; public abstract class ConstantRepaintPropertyDrawer : PropertyDrawer { SerializedObject parentSerializedObject; static readonly FieldInfo fi_m_NativeObjectPtr = typeof(SerializedObject) .GetField("m_NativeObjectPtr", BindingFlags.NonPublic | BindingFlags.Instance); static double lastUpdateTime = 0; void Repaint() { if(Framerate <= 0 || EditorApplication.timeSinceStartup > lastUpdateTime + 1 / Framerate) { lastUpdateTime = EditorApplication.timeSinceStartup; foreach (var editor in ActiveEditorTracker.sharedTracker.activeEditors) { if (editor.serializedObject == parentSerializedObject) { editor.Repaint(); OnRepaint(); return; } } } } void _OnSelectionChanged() { OnSelectionChanged(); if (parentSerializedObject == null || (IntPtr)fi_m_NativeObjectPtr.GetValue(parentSerializedObject) == IntPtr.Zero) { EditorApplication.update -= Repaint; Selection.selectionChanged -= _OnSelectionChanged; CompilationPipeline.compilationStarted -= OnCompilationStarted; CompilationPipeline.compilationFinished -= OnCompilationFinished; Undo.undoRedoPerformed -= OnUndoRedoPerformed; } } /// <summary> /// Repaintの目標フレームレート。0以下で無制限(EditorApplication.updateごと)。既定値は60。 /// </summary> protected virtual float Framerate => 60; /// <summary> /// Repaint終了時に毎回呼ばれる。 /// </summary> protected virtual void OnRepaint() { } /// <summary> /// Selection変化時に呼ばれる。 /// </summary> protected virtual void OnSelectionChanged() { } /// <summary> /// コンパイル開始時に呼ばれる。 /// </summary> protected virtual void OnCompilationStarted(object obj) { } /// <summary> /// コンパイル終了時に呼ばれる。 /// </summary> protected virtual void OnCompilationFinished(object obj) { } /// <summary> /// Undo/Redoが行われた後に呼ばれる。 /// </summary> protected virtual void OnUndoRedoPerformed() { } /// <summary> /// sealed. OnGUIの代わりにOnGUIMainをoverrideしてください。 /// </summary> public sealed override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (!ActiveEditorTracker.HasCustomEditor(property.serializedObject.targetObject)) { parentSerializedObject = property.serializedObject; EditorApplication.update -= Repaint; EditorApplication.update += Repaint; } Selection.selectionChanged -= _OnSelectionChanged; Selection.selectionChanged += _OnSelectionChanged; CompilationPipeline.compilationStarted -= OnCompilationStarted; CompilationPipeline.compilationStarted += OnCompilationStarted; CompilationPipeline.compilationFinished -= OnCompilationFinished; CompilationPipeline.compilationFinished += OnCompilationFinished; Undo.undoRedoPerformed -= OnUndoRedoPerformed; Undo.undoRedoPerformed += OnUndoRedoPerformed; OnGUIMain(position, property, label); } /// <summary> /// Override this method to make your own IMGUI based GUI for the property. /// </summary> protected virtual void OnGUIMain(Rect position, SerializedProperty property, GUIContent label) { base.OnGUI(position, property, label); } }要改善点
同じGameObject上でこのPropertyDrawerが複数回表示されている場合、その回数分Repaintが無駄に走ります。
PropertyDrawerが載ってるserializedObjectをどこかに保持しておけば比較でどうにかなりそうな気がしますが、めんどい。
あと複数選択時の挙動は未確認です。おわりに
デフォルトEditorもRequiresConstantRepaintをtrueにできればもうちょっと単純にできるのになぁと思いました。
あれは基底クラス(Editor)のvirtualメソッドの中身がreturn false;
なのでReflectionじゃどうにもならないやつ。
メソッドの中身を動的に書き換える手段とか、実はどこかにあったりするんだろうか。
- 投稿日:2020-01-22T19:47:07+09:00
SteamVRがショートカットから起動しない場合の対処法
- 投稿日:2020-01-22T18:20:33+09:00
Unity非同期処理を理解しようとしてみた
Unity非同期処理を理解しようとしてみた
Unityにおける非同期処理を理解するために、Simpleなコードから一歩づつ結果を見ていきます。
環境
- Windows10
- Unity 2019.2.11f1
- Visual Studio 2017
概要
- Unityで重い処理を実行してみる
- 重い処理を非同期実行してみる
- 重い処理の結果を待ってみる
- スレッドを追ってみる
- 後処理を正しい位置に修正
- (参考)Unity用のTaskパッケージ「UniTask」
シーン構成
シーン上にはUpdateMethodで上下に移動するだけのCubeと重い処理が実行されるボタン、そのただ2つだけがあります。
やりたいこと
- 重い処理をバックグラウンドで動かしつつ、Cubeの往復を止めない。
- 重い処理が終わったことを検知して、Cubeを消す。
Unityで重い処理を実行してみる
ここからボタンの処理を書いていきます。
最もシンプルなコードから始めます。
ボタンを押すとHeavyMethodとFinalizeMethodが順番に実行されます。
HeavyMethodは1秒Sleepするだけの単純な処理ですが、このコードを実行するとどうなるでしょうか?AsyncTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; using System.Threading.Tasks; public class AsyncTest : MonoBehaviour { public void ButtonClick() { Debug.Log("Start Click Method"); HeavyMethod(); FinalizeMethod(); Debug.Log("End Click Method"); } void HeavyMethod() { Debug.Log("Start Heavy Method"); Thread.Sleep(1000); Debug.Log("End Heavy Method"); } void FinalizeMethod() { Debug.Log("Finalize Method"); } }結果はこちらです。
そしてHeavyMethodの実行中は、Cubeの動きは止まります。
重い処理を非同期で実行してみる
やっぱり重い処理の実行中は、シーンの処理(この場合はCubeの動き)を止めて欲しくないわけです。
ということで、重い処理を非同期実行にしてみます。AsyncTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; using System.Threading.Tasks; public class AsyncTest : MonoBehaviour { public void ButtonClick() { Debug.Log("Start Click Method"); Task.Run(HeavyMethod); FinalizeMethod(); Debug.Log("End Click Method"); } void HeavyMethod() { Debug.Log("Start Heavy Method"); Thread.Sleep(1000); Debug.Log("End Heavy Method"); } void FinalizeMethod() { Debug.Log("Finalize Method"); } }
HeavyMethod();
をTask.Run(HeavyMethod);
に書き換えただけです。
これで結果がこのようになりました。
先ほどとは順番が変わっていますね。ButtonClickの中には
HeavyMethod
FinilizeMethod
となっているのに、順番が入れ替わって、
FinilizeMethod
HeavyMethod
の順番になっているわけです。
さらに言えば、Heavy Methodが終わる前に、Click Methodを抜けているのがわかります。
この実装であれば、画面のCubeの動きも止まりません。重い処理の結果を待ってみる
さてここでまた新たな需要です。
「画面の動きは止めたくないけど、Finilize MethodはHeavy Methodが終わってから実行したい。」
わがままですね。
この需要を満たすように、処理を書き換えます。AsyncTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; using System.Threading.Tasks; public class AsyncTest : MonoBehaviour { public void ButtonClick() { Debug.Log("Start Click Method"); Task.Run(HeavyMethod); // FinalizeMethod(); Debug.Log("End Click Method"); } void HeavyMethod() { Debug.Log("Start Heavy Method"); Thread.Sleep(1000); Debug.Log("End Heavy Method"); FinalizeMethod(); } void FinalizeMethod() { Debug.Log("Finalize Method"); } }
FinalizeMethod
の場所を移動しただけです。
実行結果はこちら。
この方法もまた、Cubeの移動は止まりません。
でもちょっと待ってください。
FinalizeMethod
でもし、GameObjectの操作などをしたらどうでしょう?
試してみましょう。AsyncTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; using System.Threading.Tasks; public class AsyncTest : MonoBehaviour { public void ButtonClick() { Debug.Log("Start Click Method : " + Thread.CurrentThread.ManagedThreadId); Task.Run(HeavyMethod); // FinalizeMethod(); Debug.Log("End Click Method : " + Thread.CurrentThread.ManagedThreadId); } void HeavyMethod() { Debug.Log("Start Heavy Method : " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Debug.Log("End Heavy Method : " + Thread.CurrentThread.ManagedThreadId); FinalizeMethod(); } void FinalizeMethod() { Debug.Log("Start Finalize Method : " + Thread.CurrentThread.ManagedThreadId); Destroy(GameObject.Find("Cube")); Debug.Log("Start Finalize Method : " + Thread.CurrentThread.ManagedThreadId); } }
FinalizeMethod
にCubeを破壊する処理と、おもむろにThreadのIdを出力する処理を追加しました。
結果はこちら。
あれ?
FinalizeMethod
が途中で終了していますね。
Cubeも破壊されていないし、例外も宇宙の彼方に消えてしまいました。
Task.Run()
で実行したHeavyMethod
から以降の処理のThreadのIdが切り替わってますね。非同期にした処理が、マルチスレッドで動いているのがわかります。
Unityのシーン上のオブジェクトは、メインスレッド(番号1のThread)からしか、触れることができないので、このようになってしまいました。スレッドを追ってみる
- Cubeが消えない
- 例外が消える
という問題が発生していますが、一旦「Cubeが消えない」にフォーカスして、考えましょう。
DestroyCube
をメインスレッドから実行できればいいわけです。
というわけで修正していきます。AsyncTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; using System.Threading.Tasks; public class AsyncTest : MonoBehaviour { private SynchronizationContext context; public void ButtonClick() { context = SynchronizationContext.Current; Debug.Log("Start Click Method : " + Thread.CurrentThread.ManagedThreadId); Task.Run(HeavyMethod); // FinalizeMethod(); Debug.Log("End Click Method : " + Thread.CurrentThread.ManagedThreadId); } void HeavyMethod() { Debug.Log("Start Heavy Method : " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Debug.Log("End Heavy Method : " + Thread.CurrentThread.ManagedThreadId); FinalizeMethod(); } void FinalizeMethod() { Debug.Log("Start Finalize Method : " + Thread.CurrentThread.ManagedThreadId); context.Post(state => DestroyCube(), null); // Destroy(GameObject.Find("Cube")); Debug.Log("End Finalize Method : " + Thread.CurrentThread.ManagedThreadId); } void DestroyCube() { Debug.Log("DestroyCube : " + Thread.CurrentThread.ManagedThreadId); Destroy(GameObject.Find("Cube")); } }いきなり
context = SynchronizationContext.Current
というものが出てきました。
これは、異なるThread間で状態をやりとりするためのオブジェクトです。
このcontext
を経由して、FinalizeMethod内でcontext.Post()
とすることで、メインスレッドに処理を戻すことができます。
また、本筋とは関係ありませんが、Cubeの破壊とLogの出力をまとめたMethodDestroyCube
を作成しました。結果はこちら。
ちゃんとDestoryCube
がメインスレッドで実行され、シーン内のCubeが破壊されました。
ちなみにThreadIdが先程と異なっているのは、Unityが空いてるThreadを適当にPoolからPickUpして使うからです。自分で指定しているわけではありませんよ。後処理を正しい位置に修正
さて、一応「やりたいこと」は実現できたように思いますね。
ただ、「重い処理の結果を待ってみる」でやったような、FinalizeMethod()
をHeavyMethod
内に書くのはよくない気がします。
HeavyMethod
の本来の責務ではないし、構造がネストして複雑になります。
というわけで、FinalizeMethod()
を本来あるべきButtonClick
内に戻します。AsyncTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; using System.Threading.Tasks; public class AsyncTest : MonoBehaviour { // private SynchronizationContext context; async void ButtonClick() { // context = SynchronizationContext.Current; Debug.Log("Start Click Method : " + Thread.CurrentThread.ManagedThreadId); await Task.Run(HeavyMethod); FinalizeMethod(); Debug.Log("End Click Method : " + Thread.CurrentThread.ManagedThreadId); } void HeavyMethod() { Debug.Log("Start Heavy Method : " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Debug.Log("End Heavy Method : " + Thread.CurrentThread.ManagedThreadId); // FinalizeMethod(); } void FinalizeMethod() { Debug.Log("Start Finalize Method : " + Thread.CurrentThread.ManagedThreadId); // context.Post(state => DestroyCube(), null); DestroyCube(); Debug.Log("End Finalize Method : " + Thread.CurrentThread.ManagedThreadId); } void DestroyCube() { Debug.Log("DestroyCube : " + Thread.CurrentThread.ManagedThreadId); Destroy(GameObject.Find("Cube")); } }ただ戻すだけでは、検証したように
Task.Run()
の結果をまたずにFinalizeMethod()
が動いてしまいます。
今回はTask.Run()
の前にawait
というものがありますね。これは、Task.Run()
が完了するまで次の処理を待てよ、という意味です。
これを付けると、VisualStudio2017様に「呼び出し元にasync
をつけろよ」と言われます。言われた通りにasync
をつけました。いいですね。正しい順番で動いてます。先ほどと違うのは、End Click Methodの位置でしょうか。
Task.Run()
で実行した処理の中から、メインスレッドで処理をしたいという需要意外では、SynchronizationContext
を使う必要はないってことなんですね。
これならDestroyCube
で発生した例外もcatchできます。Unity用のTask(UniTask)
今までは使ってきたTaskという仕組みは、C#に備わっている仕組みです。Unityの仕組みではありません。
これはusing System.Threading.Tasks;
をimportしていることからもわかりますね。
実は、UnityにはTaskをもっとUnityに特化させたUniTaskというものがあるようです。
これを使うと、処理が速くなるだけではなく後々いろいろメリットがありそうです。
合わせて、今回は自作メソッドごとTask.Run()
で呼び出しましたが、今回言及しなかった、別スレッドでの処理の例外をメインスレッドで受けるためにも「UniTaskCompletionSource」などを使って直接awaitできるように修正したら、よりいいのではないでしょうか。使い方
UniTaskをGitHubから拾ってきて、UnityPackageをimportしたら、using UniRx.Async;
を書く。それでは。
- 投稿日:2020-01-22T17:27:15+09:00
Unityエディタ上で表示される自動実装プロパティの名前がいい感じに
- 投稿日:2020-01-22T15:27:31+09:00
MRTK v2.2 + Scripting Backend .NETで嵌ったはなし
この記事を必要とするひと
- HoloLens/HoloLens2でMRTKv2.2を使いたい
- ScriptingBackend .NETでデバッグをしたい
この記事を必要としないひと
- IL2CPPのビルド時間に耐えられる
- ScriptingBackend .NETでデバッグなんかしない
- MRTKv2.1で十分である
開発環境
- Unity 2018.4.15f
- Visual Studio 2019
- MRTKv2.2
経緯
MRTKv2.2でUnity上での開発を完了させ、実機(HoloLens2)にデプロイすべくBuildしたら詰んだ。
何を言われているのか分かるが判らなくて豆腐さえ置けない。
— シュレディンガーのナツさん (@natsu_san) January 21, 2020
MRTKver2.2でMSBuildとか入ったっぽくてそれの関連????#HoloLens #Unity pic.twitter.com/KelZ0wbxIN解決方法(簡潔に)
- MRTKv2.2で追加されたMSBuildを使わない(ただし、別の制約あり)
- MRTKv2.1でBuildしてからMRTKv2.2に差し替える(未確認)
こんにちは。自分も全く同じ状況に遭いました。
— Cindy / キンキン (@kinkincindy) January 22, 2020
試しにmrtk2.1.0を入れてビルドして、削除し、次にmrtk2.2.0を入れてましたらビルドできました。あまり賢い方法とは言えませんが、参考にしていただければ幸いです。NGな手順
新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject でMSBuildにチェックを入れる(デフォルト)
ScriptingBackendを.NETにする
Editor上でお豆腐を置く(ここまでは問題なし)
Build
突然の死。
Assets\MixedRealityToolkit.Providers\WindowsMixedReality\WindowsMixedRealityReprojectionUpdater.cs(61,35): error CS0012: The type 'Enum' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.
「.netStandard2のassembly参照が無いよ!」
んなわけあるかーーwwwwMSBuild > Regenelate C# SDK Projectしてみる
Dependencies.msb4u.csproj : error NU1101: �p�b�P�[�W MSBuildForUnity �����‚���܂���B�\�[�X Microsoft Visual Studio Offline Packages, nuget.org �ɂ́A���� ID �̃p�b�P�[�W�����݂��܂���B
UnityEngine.Debug:LogError(Object)ちょっと何言ってるか分からないですね。。。
救いの手
そこは沼。。。
— みう (16) (@miunet0123) January 21, 2020
・MRTK は .unitypackage で入れる
・MS Build for Unity は有効化しない
のふたつで、たぶん。。。 https://t.co/6VIVF47E0D解決策:MSBuild使わない
新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject で MSBuildのチェックを外す
ScriptingBackendを.NETにする
Editor上でお豆腐を置く(なんとなく青)
Build!
VisualStudioで開く→デバッグなしで開始
......。
Visual Studio 2017 Update3でC#7.1を使ってみた
アッ、ハイ!気を取り直してVisualStudio 2019 で開く→デバッグなしで開始
バーチャル豆腐きた!
考察
MSBuildは依存系DLLを集めてくれるらしい。
MSBuildを有効にすると下記のメッセージが表示される。DLL探索パスにDotNetAdaptorのフォルダを追加したよ~
おそらく、ここにDLLが入る想定なのだが、何らかの理由で上手く入らないパターンがある。
制約
Microsoft Mixed Reality Toolkit Release Notes
曰く
MSBuildはNugetで自動的に依存を解決するよ
いくつかの機能はMSBuild必須だよ(例えばHoloLens2のhand aned eye remoting)ぬぬ。
Holographic Remotingでハンドトラッキングとアイトラッキングをサポートしたよ!
ぐぬぬ。
ちなみに、NGパターンでもIL2CPPだと普通にビルドできます。
が、すごーーーーーーーーく待たされる。辛い。MRTK supports both IL2CPP and .NET scripting backends on Unity 2018
MSさんなんとかしてよね!
あともっと建設的な解決策がありましたらどなたかご教授くださいm(_ _)m
- 投稿日:2020-01-22T15:27:31+09:00
MRTKv2.2 + ScriptingBackend .NETで嵌ったはなし
この記事を必要とするひと
- HoloLens/HoloLens2でMRTKv2.2を使いたい
- ScriptingBackend .NETでデバッグをしたい
この記事を必要としないひと
- IL2CPPのビルド時間に耐えられる
- ScriptingBackend .NETでデバッグなんかしない
- MRTKv2.1で十分である
開発環境
- Unity 2018.4.15f
- Visual Studio 2019
- MRTKv2.2
経緯
MRTKv2.2でUnity上での開発を完了させ、実機(HoloLens2)にデプロイすべくBuildしたら詰んだ。
何を言われているのか分かるが判らなくて豆腐さえ置けない。
— シュレディンガーのナツさん (@natsu_san) January 21, 2020
MRTKver2.2でMSBuildとか入ったっぽくてそれの関連????#HoloLens #Unity pic.twitter.com/KelZ0wbxIN解決方法(簡潔に)
- MRTKv2.2で追加されたMSBuildを使わない(ただし、別の制約あり)
- MRTKv2.1でBuildしてからMRTKv2.2に差し替える(未確認)
こんにちは。自分も全く同じ状況に遭いました。
— Cindy / キンキン (@kinkincindy) January 22, 2020
試しにmrtk2.1.0を入れてビルドして、削除し、次にmrtk2.2.0を入れてましたらビルドできました。あまり賢い方法とは言えませんが、参考にしていただければ幸いです。NGな手順
新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject でMSBuildにチェックを入れる(デフォルト)
ScriptingBackendを.NETにする
Editor上でお豆腐を置く(ここまでは問題なし)
Build
突然の死。
Assets\MixedRealityToolkit.Providers\WindowsMixedReality\WindowsMixedRealityReprojectionUpdater.cs(61,35): error CS0012: The type 'Enum' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.
「.netStandard2のassembly参照が無いよ!」
んなわけあるかーーwwwwMSBuild > Regenelate C# SDK Projectしてみる
Dependencies.msb4u.csproj : error NU1101: �p�b�P�[�W MSBuildForUnity �����‚���܂���B�\�[�X Microsoft Visual Studio Offline Packages, nuget.org �ɂ́A���� ID �̃p�b�P�[�W�����݂��܂���B
UnityEngine.Debug:LogError(Object)ちょっと何言ってるか分からないですね。。。
救いの手
そこは沼。。。
— みう (16) (@miunet0123) January 21, 2020
・MRTK は .unitypackage で入れる
・MS Build for Unity は有効化しない
のふたつで、たぶん。。。 https://t.co/6VIVF47E0D解決策:MSBuild使わない
新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject で MSBuildのチェックを外す
ScriptingBackendを.NETにする
Editor上でお豆腐を置く(なんとなく青)
Build!
VisualStudioで開く→デバッグなしで開始
......。
Visual Studio 2017 Update3でC#7.1を使ってみた
アッ、ハイ!気を取り直してVisualStudio 2019 で開く→デバッグなしで開始
バーチャル豆腐きた!
考察
MSBuildは依存系DLLを集めてくれるらしい。
MSBuildを有効にすると下記のメッセージが表示される。DLL探索パスにDotNetAdaptorのフォルダを追加したよ~
おそらく、ここにDLLが入る想定なのだが、何らかの理由で上手く入らないパターンがある。
制約
Microsoft Mixed Reality Toolkit Release Notes
曰く
MSBuildはNugetで自動的に依存を解決するよ
いくつかの機能はMSBuild必須だよ(例えばHoloLens2のhand aned eye remoting)ぬぬ。
Holographic Remotingでハンドトラッキングとアイトラッキングをサポートしたよ!
ぐぬぬ。
ちなみに、NGパターンでもIL2CPPだと普通にビルドできます。
が、すごーーーーーーーーく待たされる。辛い。MRTK supports both IL2CPP and .NET scripting backends on Unity 2018
MSさんなんとかしてよね!
あともっと建設的な解決策がありましたらどなたかご教授くださいm(_ _)m
- 投稿日:2020-01-22T14:01:12+09:00
unityroomに公開したやつ(蓄積メモ)
2020/01/22
爆速でunitychan入れてうごかしたやつ。ゲーム性はない。
https://unityroom.com/games/otameshiunitychan
- 投稿日:2020-01-22T13:26:37+09:00
いったりきたりするオブジェクトをつくる
定型文みたいなものなので、基本コピペでいいと思います
using UnityEngine; using System.Collections; public class ittarikitari : MonoBehaviour { [SerializeField] private Vector3 _startPosition; [SerializeField] private Vector3 _targetPosition; [SerializeField] private float _duration = 1f; private float _time = 0; //private int _dirFactor = 1; private bool _inverse = false; private void Update() { _time += Time.deltaTime; // 指定時間を過ぎたら向きを逆に if (_time >= _duration) { _time = 0; _inverse = !_inverse; } // 時間を媒介変数として計算(0 - 1) float t = _time / _duration; if (_inverse) { transform.position = Vector3.Lerp(_startPosition, _targetPosition, 1f - t); } else { transform.position = Vector3.Lerp(_startPosition, _targetPosition, t); } } }
- 投稿日:2020-01-22T07:22:49+09:00
Unity 向けの vscode プラグインメモ
C# for Visual Studio Code
- シンタックスハイライト
- コード補完
- デバッグ
C# Extensions
- 定義もとへのジャンプ
C# FixFormat
- コードをフォーマットしてくれる
- "editor.formatOnSave": true とすると、セーブ時に勝手にやってくれる
Unity Code Snippets
- Unity 向けのスニペットが使用可能になる
Debugger for Unity
- デバッグ実行用
Code Spell Checker
- スペルチェックをしてくれる
- 辞書登録もできて便利
Bracket Pair Colorizer
- カッコを色付けしてくれる
- 視認性が高くなる
- 投稿日:2020-01-22T00:59:06+09:00
Rider で探る、このメソッドどのボタンから呼ばれているの?
Unity の IDE に Rider 使うと楽になったな~と思っているショートカットの紹介シリーズです。
前提
- Windows です
- ショートカットキーの設定が Reshaper バインドです
このメソッド、どこのボタンから呼ばれているの?
人の書いたプログラムを読んでいて、このメソッド、どこのボタンから呼ばれているの?って思ったことありませんか?
この3ステップやるのにどのぐらいかかると思いますか?
1. ファイル開いて
2. 目的のメソッドに飛んで、
3. そのメソッドが使われているボタンまで飛ぶこれ、Rider なら15秒でできます。
1. Ctrl+N してポップアップにファイル名
2. F12 してポップアップにメソッド名
3. Alt+F7 で設定されているボタンに飛ぶ(複数個所から使用されている場合は候補が出る感じになります。)
うっすら覚えている少し前のプロジェクトや、知らない人のプログラム読むの早くなります。
捕捉
Mac で IntelliJ IDEA バインドだと以下のような感じになりますが、結局ステップ数は同じです。
1. shift + shift してポップアップにファイル名
2. command + F12 してポップアップにメソッド名
3. option + Alt + F7 で設定されているボタンに飛ぶ