20200122のUnityに関する記事は10件です。

【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を選択してみましょう。
image.png
ダメみたいですね……
_unity_selfなるものがnullだそうです。知らんがな。

VisualStudioでエラー箇所を確認してみると、
image.png
なんと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.cs
using 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じゃどうにもならないやつ。
メソッドの中身を動的に書き換える手段とか、実はどこかにあったりするんだろうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SteamVRがショートカットから起動しない場合の対処法

SteamVRが起動中のため起動できない

PCを起動したばかりなのに,SteamVRが起動している扱いになって,ショートカットから起動できない場合があります

いくつかのPCで確認しています
原因究明はできていませんが,対象法がわかったので共有します

  • SteamVRのprehabが入ったunityのSceneを起動する
  • 何かしらのVRアプリケーションを実行する

すると,SteamVRが起動し,いつものWindowが表示されます

自分のPCでも発生していたのですが,なぜか通常通りに動作してしまうので,写真・詳細追加は後ほど

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity非同期処理を理解しようとしてみた

Unity非同期処理を理解しようとしてみた

Unityにおける非同期処理を理解するために、Simpleなコードから一歩づつ結果を見ていきます。

環境

  • Windows10
  • Unity 2019.2.11f1
  • Visual Studio 2017

概要

  1. Unityで重い処理を実行してみる
  2. 重い処理を非同期実行してみる
  3. 重い処理の結果を待ってみる
  4. スレッドを追ってみる
  5. 後処理を正しい位置に修正
  6. (参考)Unity用のTaskパッケージ「UniTask」

シーン構成

シーン上にはUpdateMethodで上下に移動するだけのCubeと重い処理が実行されるボタン、そのただ2つだけがあります。

image.png

やりたいこと

  • 重い処理をバックグラウンドで動かしつつ、Cubeの往復を止めない。
  • 重い処理が終わったことを検知して、Cubeを消す。

Unityで重い処理を実行してみる

ここからボタンの処理を書いていきます。
最もシンプルなコードから始めます。
ボタンを押すとHeavyMethodとFinalizeMethodが順番に実行されます。
HeavyMethodは1秒Sleepするだけの単純な処理ですが、このコードを実行するとどうなるでしょうか?

AsyncTest.cs
using 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の動きは止まります。
image.png

重い処理を非同期で実行してみる

やっぱり重い処理の実行中は、シーンの処理(この場合はCubeの動き)を止めて欲しくないわけです。
ということで、重い処理を非同期実行にしてみます。

AsyncTest.cs
using 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);に書き換えただけです。
これで結果がこのようになりました。
image.png

先ほどとは順番が変わっていますね。ButtonClickの中には

  1. HeavyMethod
  2. FinilizeMethod

となっているのに、順番が入れ替わって、

  1. FinilizeMethod
  2. HeavyMethod

の順番になっているわけです。

さらに言えば、Heavy Methodが終わる前に、Click Methodを抜けているのがわかります。
この実装であれば、画面のCubeの動きも止まりません。

重い処理の結果を待ってみる

さてここでまた新たな需要です。
「画面の動きは止めたくないけど、Finilize MethodはHeavy Methodが終わってから実行したい。」
わがままですね。
この需要を満たすように、処理を書き換えます。

AsyncTest.cs
using 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の場所を移動しただけです。
実行結果はこちら。
image.png

この方法もまた、Cubeの移動は止まりません。

でもちょっと待ってください。FinalizeMethodでもし、GameObjectの操作などをしたらどうでしょう?
試してみましょう。

AsyncTest.cs
using 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を出力する処理を追加しました。
結果はこちら。
image.png
あれ?
FinalizeMethodが途中で終了していますね。
Cubeも破壊されていないし、例外も宇宙の彼方に消えてしまいました。
Task.Run()で実行したHeavyMethodから以降の処理のThreadのIdが切り替わってますね。非同期にした処理が、マルチスレッドで動いているのがわかります。
Unityのシーン上のオブジェクトは、メインスレッド(番号1のThread)からしか、触れることができないので、このようになってしまいました。

スレッドを追ってみる

  1. Cubeが消えない
  2. 例外が消える

という問題が発生していますが、一旦「Cubeが消えない」にフォーカスして、考えましょう。
DestroyCubeをメインスレッドから実行できればいいわけです。
というわけで修正していきます。

AsyncTest.cs
using 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を作成しました。

結果はこちら。
image.png
ちゃんとDestoryCubeがメインスレッドで実行され、シーン内のCubeが破壊されました。
ちなみにThreadIdが先程と異なっているのは、Unityが空いてるThreadを適当にPoolからPickUpして使うからです。自分で指定しているわけではありませんよ。

後処理を正しい位置に修正

さて、一応「やりたいこと」は実現できたように思いますね。
ただ、「重い処理の結果を待ってみる」でやったような、FinalizeMethod()HeavyMethod内に書くのはよくない気がします。
HeavyMethodの本来の責務ではないし、構造がネストして複雑になります。
というわけで、FinalizeMethod()を本来あるべきButtonClick内に戻します。

AsyncTest.cs
using 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をつけました。

結果はこちら。
image.png

いいですね。正しい順番で動いてます。先ほどと違うのは、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;を書く。

それでは。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityエディタ上で表示される自動実装プロパティの名前がいい感じに

Unityエディタ上の自動実装プロパティが変な名前になっていた(今まで)

自動実装プロパティを使いたい、そしてシリアライズしてUnityエディタ上で値を編集したい。
そんなとき、以下のように書くことで一応実現できました。

    [field: SerializeField]
    public int Hogehoge { get; set; }

ですが、インスペクター上の表示はこんな感じでイケてない。
property000.PNG

プロパティ名が正しく表示されるように

まだα版ではありますが、ふと思い立ってUnity2020.1.19aで確認してみたらいい感じの表示になっていました。
property001.PNG

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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したら詰んだ。

解決方法(簡潔に)

  • MRTKv2.2で追加されたMSBuildを使わない(ただし、別の制約あり)
  • MRTKv2.1でBuildしてからMRTKv2.2に差し替える(未確認)

NGな手順

新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject でMSBuildにチェックを入れる(デフォルト)
クリップボード-11.png
ScriptingBackendを.NETにする
クリップボード-9.png
Editor上でお豆腐を置く(ここまでは問題なし)
クリップボード-13.png
Build
クリップボード-15.png
突然の死。
クリップボード-14.png

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参照が無いよ!」
んなわけあるかーーwwww

MSBuild > Regenelate C# SDK Projectしてみる

クリップボード-16.png

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)

ちょっと何言ってるか分からないですね。。。

救いの手

解決策:MSBuild使わない

新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject で MSBuildのチェックを外す

クリップボード-1.png
ScriptingBackendを.NETにする
Editor上でお豆腐を置く(なんとなく青)
クリップボード-2.png
Build!
VisualStudioで開く→デバッグなしで開始
クリップボード-6.png
......。
Visual Studio 2017 Update3でC#7.1を使ってみた
アッ、ハイ!

気を取り直してVisualStudio 2019 で開く→デバッグなしで開始
20200122_132522_HoloLens.jpg

バーチャル豆腐きた!

考察

MSBuildは依存系DLLを集めてくれるらしい。
MSBuildを有効にすると下記のメッセージが表示される。

クリップボード-12.png

DLL探索パスにDotNetAdaptorのフォルダを追加したよ~

おそらく、ここにDLLが入る想定なのだが、何らかの理由で上手く入らないパターンがある。

制約

Microsoft Mixed Reality Toolkit Release Notes
曰く
クリップボード-5.png

MSBuildはNugetで自動的に依存を解決するよ
いくつかの機能はMSBuild必須だよ(例えばHoloLens2のhand aned eye remoting)

ぬぬ。

クリップボード-7.png

Holographic Remotingでハンドトラッキングとアイトラッキングをサポートしたよ!

ぐぬぬ。

ちなみに、NGパターンでもIL2CPPだと普通にビルドできます。
が、すごーーーーーーーーく待たされる。辛い。

MRTK supports both IL2CPP and .NET scripting backends on Unity 2018

MSさんなんとかしてよね!
あともっと建設的な解決策がありましたらどなたかご教授くださいm(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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したら詰んだ。

解決方法(簡潔に)

  • MRTKv2.2で追加されたMSBuildを使わない(ただし、別の制約あり)
  • MRTKv2.1でBuildしてからMRTKv2.2に差し替える(未確認)

NGな手順

新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject でMSBuildにチェックを入れる(デフォルト)
クリップボード-11.png
ScriptingBackendを.NETにする
クリップボード-9.png
Editor上でお豆腐を置く(ここまでは問題なし)
クリップボード-13.png
Build
クリップボード-15.png
突然の死。
クリップボード-14.png

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参照が無いよ!」
んなわけあるかーーwwww

MSBuild > Regenelate C# SDK Projectしてみる

クリップボード-16.png

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)

ちょっと何言ってるか分からないですね。。。

救いの手

解決策:MSBuild使わない

新規プロジェクト作成
SwichPlatform(UWP)
Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackageインポート
MixedRealityToolkit > ConfigureUnityProject で MSBuildのチェックを外す

クリップボード-1.png
ScriptingBackendを.NETにする
Editor上でお豆腐を置く(なんとなく青)
クリップボード-2.png
Build!
VisualStudioで開く→デバッグなしで開始
クリップボード-6.png
......。
Visual Studio 2017 Update3でC#7.1を使ってみた
アッ、ハイ!

気を取り直してVisualStudio 2019 で開く→デバッグなしで開始
20200122_132522_HoloLens.jpg

バーチャル豆腐きた!

考察

MSBuildは依存系DLLを集めてくれるらしい。
MSBuildを有効にすると下記のメッセージが表示される。

クリップボード-12.png

DLL探索パスにDotNetAdaptorのフォルダを追加したよ~

おそらく、ここにDLLが入る想定なのだが、何らかの理由で上手く入らないパターンがある。

制約

Microsoft Mixed Reality Toolkit Release Notes
曰く
クリップボード-5.png

MSBuildはNugetで自動的に依存を解決するよ
いくつかの機能はMSBuild必須だよ(例えばHoloLens2のhand aned eye remoting)

ぬぬ。

クリップボード-7.png

Holographic Remotingでハンドトラッキングとアイトラッキングをサポートしたよ!

ぐぬぬ。

ちなみに、NGパターンでもIL2CPPだと普通にビルドできます。
が、すごーーーーーーーーく待たされる。辛い。

MRTK supports both IL2CPP and .NET scripting backends on Unity 2018

MSさんなんとかしてよね!
あともっと建設的な解決策がありましたらどなたかご教授くださいm(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

unityroomに公開したやつ(蓄積メモ)


2020/01/22
爆速でunitychan入れてうごかしたやつ。ゲーム性はない。
https://unityroom.com/games/otameshiunitychan


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いったりきたりするオブジェクトをつくる

定型文みたいなものなので、基本コピペでいいと思います

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);
        }
    }
}

コンポーネントの見た目はこんな感じ
j.png

いったりきたりする
k.png
l.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • カッコを色付けしてくれる
  • 視認性が高くなる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rider で探る、このメソッドどのボタンから呼ばれているの?

Unity の IDE に Rider 使うと楽になったな~と思っているショートカットの紹介シリーズです。

前提

  • Windows です
  • ショートカットキーの設定が Reshaper バインドです

このメソッド、どこのボタンから呼ばれているの?

人の書いたプログラムを読んでいて、このメソッド、どこのボタンから呼ばれているの?って思ったことありませんか?

この3ステップやるのにどのぐらいかかると思いますか?
1. ファイル開いて
2. 目的のメソッドに飛んで、
3. そのメソッドが使われているボタンまで飛ぶ

これ、Rider なら15秒でできます。
1. Ctrl+N してポップアップにファイル名
2. F12 してポップアップにメソッド名
3. Alt+F7 で設定されているボタンに飛ぶ(複数個所から使用されている場合は候補が出る感じになります。)
3step.gif

うっすら覚えている少し前のプロジェクトや、知らない人のプログラム読むの早くなります。

捕捉

Mac で IntelliJ IDEA バインドだと以下のような感じになりますが、結局ステップ数は同じです。
1. shift + shift してポップアップにファイル名
2. command + F12 してポップアップにメソッド名
3. option + Alt + F7 で設定されているボタンに飛ぶ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む