MR 入力 213:モーション コントローラー

Note

Mixed Reality Academy のチュートリアルは、HoloLens (第 1 世代) と Mixed Reality イマーシブ ヘッドセットを念頭に置いて編成されています。 そのため、それらのデバイスの開発に関するガイダンスを引き続き探している開発者のために、これらのチュートリアルをそのまま残しておくことが重要だと考えています。 これらのチュートリアルが、HoloLens 2 に使用されている最新のツールセットや操作に更新されることは "ありません"。 これらは、サポートされているデバイス上で継続して動作するように、保守されます。 HoloLens 2 向けには、新しいチュートリアル シリーズが投稿されています。

Mixed Reality のモーション コントローラーによって、さらにハイ レベルの対話機能が追加されます。 モーション コントローラーを使用すると、リアル タイムでの物理的な操作のように、より自然な方法でオブジェクトを直接操作できるため、没入感が高まり、より楽しくアプリを使用できます。

MR 入力 213 では、単純な空間描画エクスペリエンスを作成して、モーション コントローラーの入力イベントについて詳細を確認します。 このアプリでユーザーは、さまざまな種類のブラシと色を使用して 3 次元空間で描画できます。

このチュートリアルで説明するトピック

MixedReality213 Topic1 MixedReality213 Topic2 MixedReality213 Topic3
コントローラーの視覚化 コントローラーの入力イベント カスタム コントローラーと UI
Unity のゲーム モードとランタイムでモーション コントローラー モデルをレンダリングする方法を説明します。 さまざまな種類のボタン イベントとその応用について説明します。 コントローラー上に UI 要素を重ね合わせる方法や、UI 要素を完全にカスタマイズする方法を説明します。

デバイス サポート

コース HoloLens イマーシブ ヘッドセット
MR 入力 213:モーション コントローラー ✔️

開始する前に

前提条件

このページにある、イマーシブ ヘッドセットのインストール チェックリストを参照してください。

プロジェクト ファイル

Note

ダウンロードする前にソース コードを確認する場合は、GitHub で参照できます。

Unity のセットアップ

目標

  • Windows Mixed Reality 開発用に Unity を最適化する
  • Mixed Reality カメラをセットアップする
  • 環境をセットアップします

Instructions

  • Unity を起動します。

  • [Open] を選択します。

  • デスクトップに移動し、先ほどアーカイブ解除した MixedReality213-master フォルダーを見つけます。

  • [フォルダーの選択] をクリックします。

  • Unity でプロジェクト ファイルの読み込みが完了すると、Unity エディターを表示できるようになります。

  • Unity で、[File] (ファイル) > [Build Settings] (ビルド設定) の順に選択します。

    MR213_BuildSettings

  • [プラットフォーム] リストで [ユニバーサル Windows プラットフォーム] を選択し、[プラットフォームの切り替え] ボタンをクリックします。

  • [ターゲット デバイス] を [任意のデバイス] に設定します

  • [Build Type](ビルドの種類) を [D3D] に設定します

  • SDK を [最新のインストール] に設定します

  • [Unity C# プロジェクト] をオンにします

    • これにより、Unity プロジェクトをリビルドせずに、Visual Studio プロジェクトでスクリプト ファイルを変更できます。
  • [プレーヤー設定] をクリックします。

  • [インスペクター] パネルで最下部までスクロールします

  • [XR 設定] で [仮想現実がサポート済み] をオンにします

  • [Virtual Reality SDK] で [Windows Mixed Reality] を選択します

    MR213_XRSettings

  • [ビルド設定] ウィンドウを閉じます。

プロジェクトの構造

このチュートリアルでは、Mixed Reality ツールキット - Unity を使用します。 こちらのページでリリースを確認できます。

ProjectStructure

参照用の完成済みシーン

  • [Scenes] フォルダーには、完成した 2 つの Unity シーンがあります。
    • MixedReality213: 1 つのブラシを使用した完成済みシーン
    • MixedReality213Advanced: 複数のブラシを使用した高度なデザイン用の完成済みシーン

チュートリアル用の新しいシーンの設定

  • Unity で [ファイル] > [新しいシーン] をクリックします

  • [メイン カメラ][指向性ライト] を削除します

  • [プロジェクト] パネルで、次のプレハブを検索し、[階層] パネルにドラッグします。

    • Assets/HoloToolkit/Input/Prefabs/MixedRealityCamera
    • Assets/AppPrefabs/Environment

    カメラと環境

  • Mixed Reality ツールキットには、2 つのカメラ プレハブがあります。

    • MixedRealityCamera.prefab: カメラのみ
    • MixedRealityCameraParent.prefab: カメラ、テレポート、および境界
    • このチュートリアルでは、テレポート機能なしの MixedRealityCamera を使用します。 このため、ユーザーが使用しやすいように、基本フロアを含む単純な環境プレハブを追加しました。
    • MixedRealityCameraParent でのテレポートの詳細については、「高度な設計 - テレポートと移動」を参照してください

スカイボックスのセットアップ

  • [ウィンドウ] > [照明] > [設定] をクリックします

  • [スカイボックス素材] フィールドの右側にある円をクリックします

  • 「gray」と入力し、[SkyboxGray] (Assets/AppPrefabs/Support/Materials/SkyboxGray.mat) を選択します

    スカイボックスの設定

  • [スカイボックス] オプションをオンにして、割り当てられている灰色のグラデーション スカイボックスが表示されるようにします

    スカイボックスの切り替えオプション

  • MixedRealityCamera、環境、灰色のスカイボックスのシーンは、次のようになります。

    MixedReality213 環境

  • [ファイル] > [名前を付けてシーンを保存] をクリックします

  • シーンに名前を付け、[Scenes] フォルダーに保存します

第 1 章 - コントローラーの視覚化

目標

  • Unity のゲーム モードとランタイムでモーション コントローラー モデルをレンダリングする方法について説明します。

Windows Mixed Reality には、コントローラーを視覚化するためのアニメーション コントローラー モデルが用意されています。 アプリでコントローラーを視覚化するには、いくつかの方法があります。

  • 既定値 - 既定のコントローラーを変更せずに使用する
  • ハイブリッド - 既定のコントローラーを使用するが、その要素の一部をカスタマイズするか、UI コンポーネントをオーバーレイする
  • 置換 - コントローラーに、カスタマイズした独自の 3D モデルを使用する

この章では、これらのコントローラーのカスタマイズの例について説明します。

Instructions

  • [プロジェクト] パネルで、検索ボックスに「MotionControllers」と入力します。 Assets/HoloToolkit/Input/Prefabs/ の下で見つけることもできます。
  • MotionControllers プレハブを [階層] パネルにドラッグします。
  • [階層] パネルで MotionControllers プレハブをクリックします。

MotionControllers プレハブ

MotionControllers プレハブには、代替コントローラー モデル用のスロットを提供する MotionControllerVisualizer スクリプトがあります。 手や剣などの独自のカスタム 3D モデルを割り当て、[常に代替左/右モデルを使用] をオンにすると、既定のモデルではなく、それらが表示されます。 第 4 章では、このスロットを使用して、コントローラー モデルをブラシに置き換えます。

MR213_ControllerVisualizer

手順

  • [インスペクター] パネルで MotionControllerVisualizer スクリプトをダブルクリックして、コードを Visual Studio で表示します

MotionControllerVisualizer スクリプト

MotionControllerVisualizer クラスと MotionControllerInfo クラスは、既定のコントローラー モデルを変更 & アクセスする手段を提供します。 MotionControllerVisualizer は、Unity の InteractionSourceDetected イベントにサブスクライブし、コントローラー モデルが検出されると、それらのモデルを自動的にインスタンス化します。

protected override void Awake()
{
    ...
    InteractionManager.InteractionSourceDetected += InteractionManager_InteractionSourceDetected;
    InteractionManager.InteractionSourceLost += InteractionManager_InteractionSourceLost;
    ...
}

コントローラー モデルは、glTF 仕様に従って提供されています。 この形式は、3D アセットの送信とアンパックの背後にあるプロセスを改善する一方で、共通の形式を提供するために作成されました。 この場合、実行時にコントローラー モデルを取得し、読み込む必要があります。これは、ユーザーのエクスペリエンスを可能な限りシームレスする必要がある一方で、どのバージョンのモーション コントローラーをユーザーが使用しているか保証されないためです。 このコースでは、Mixed Reality ツールキットを通じて、Khronos Group の UnityGLTF プロジェクトの 1 つのバージョンを使用します。

コントローラーが提供されると、スクリプトは MotionControllerInfo を使用して、特定のコントローラー要素の変換を検出できます。これにより、スクリプトはそれらの要素自体を正しく配置できます。

後の章では、これらのスクリプトを使用して UI 要素をコントローラーにアタッチする方法を説明します。

一部のスクリプトには、#if !UNITY_EDITOR または UNITY_WSA を含むコード ブロックがあります。 これらのコード ブロックは、Windows にデプロイするときに UWP ランタイムでのみ実行されます。 これは、Unity エディターと UWP アプリ ランタイムで使用される API のセットが異なるためです。

  • シーンを保存し、[再生] ボタンをクリックします。

ヘッドセットで、モーション コントローラーを含むシーンを確認できます。 ボタンのクリック、サムスティックの動き、タッチパッドのタッチ強調表示の詳細なアニメーションを確認できます。

MR213_Controller視覚化の既定値

第 2 章 - コントローラーへの UI 要素のアタッチ

目標

  • モーション コントローラーの要素について学習する
  • コントローラーの特定の部分にオブジェクトをアタッチする方法を学習する

この章では、ユーザーがいつでも簡単にアクセスし、操作できるユーザー インターフェイス要素をコントローラーに追加する方法について説明します。 また、タッチパッド入力を使用する単純なカラー ピッカー UI を追加する方法についても説明します。

Instructions

  • [プロジェクト] パネルで MotionControllerInfo スクリプトを検索します。
  • 検索結果で MotionControllerInfo スクリプトをダブルクリックして、Visual Studio でコードを表示します。

MotionControllerInfo スクリプト

最初の手順は、UI をアタッチする先のコントローラーの要素を選択することです。 これらの要素は、MotionControllerInfo.csControllerElementEnum で定義されています。

MR213 MotionControllerElements

  • ホーム
  • Menu
  • Grasp
  • Thumbstick
  • Select
  • タッチパッド
  • Pointing pose - この要素は、前方をポイントするコントローラーの先端を表します。

手順

  • [プロジェクト] パネルで AttachToController スクリプトを検索します。
  • 検索結果で AttachToController スクリプトをダブルクリックして、Visual Studio でコードを表示します。

AttachToController スクリプト

AttachToController スクリプトを使用すると、任意のオブジェクトを、指定したコントローラーの利き手と要素に簡単にアタッチできます。

AttachElementToController() で、以下を行います。

  • MotionControllerInfo.Handedness を使用して利き手を確認する
  • MotionControllerInfo.TryGetElement() を使用してコントローラーの特定の要素を取得する
  • コントローラー モデルから要素の変換を取得した後、その下のオブジェクトを親にし、オブジェクトのローカル位置 & 回転を 0 に設定します。
public MotionControllerInfo.ControllerElementEnum Element { get { return element; } }

private void AttachElementToController(MotionControllerInfo newController)
{
     if (!IsAttached && newController.Handedness == handedness)
     {
          if (!newController.TryGetElement(element, out elementTransform))
          {
               Debug.LogError("Unable to find element of type " + element + " under controller " + newController.ControllerParent.name + "; not attaching.");
               return;
          }

          controller = newController;

          SetChildrenActive(true);

          // Parent ourselves under the element and set our offsets
          transform.parent = elementTransform;
          transform.localPosition = positionOffset;
          transform.localEulerAngles = rotationOffset;
          if (setScaleOnAttach)
          {
               transform.localScale = scale;
          }

          // Announce that we're attached
          OnAttachToController();
          IsAttached = true;
     }
}

AttachToController スクリプトを使用する最も簡単な方法は、ColorPickerWheel で実行したように、このスクリプトを継承することです。OnAttachToController 関数と OnDetachFromController 関数をオーバーライドするだけで、コントローラーが検出/切断されたときにセットアップ/機能停止を実行できます。

手順

  • [プロジェクト] パネルで、検索ボックスに「ColorPickerWheel」と入力します。 これは、Assets/AppPrefabs/ の下で見つけることもできます。
  • ColorPickerWheel プレハブを [階層] パネルにドラッグします。
  • [階層] パネルで ColorPickerWheel プレハブをクリックします。
  • [インスペクター] パネルで ColorPickerWheel スクリプトをダブルクリックして、コードを Visual Studio で表示します。

ColorPickerWheel プレハブ

ColorPickerWheel スクリプト

ColorPickerWheelAttachToController を継承するため、[利き手][要素][インスペクター] パネルに表示されます。 左のコントローラーの Touchpad 要素に UI をアタッチします。

ColorPickerWheel スクリプト

ColorPickerWheel は、OnAttachToControllerOnDetachFromController をオーバーライドして入力イベントにサブスクライブします。次の章では、これを使用して、タッチパッド入力で色を選択します。

public class ColorPickerWheel : AttachToController, IPointerTarget
{
    protected override void OnAttachToController()
    {
        // Subscribe to input now that we're parented under the controller
        InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
    }

    protected override void OnDetachFromController()
    {
        Visible = false;

        // Unsubscribe from input now that we've detached from the controller
        InteractionManager.InteractionSourceUpdated -= InteractionSourceUpdated;
    }
    ...
}
  • シーンを保存し、[再生] ボタンをクリックします。

コントローラーにオブジェクトをアタッチする別の方法

スクリプトで AttachToController を継承し、OnAttachToController をオーバーライドすることをお勧めします。 ただし、これが常に可能であるとは限りません。 もう 1 つの方法は、スタンドアロン コンポーネントとしてこれを使用することです。 これは、スクリプトをリファクタリングせずに、既存のプレハブをコントローラーにアタッチする場合に便利です。 IsAttached が true に設定されるまでクラスを待機させ、その後にセットアップを実行するだけです。 これを行う最も簡単な方法は、"Start" のコルーチンを使用する方法です。

private IEnumerator Start() {
    AttachToController attach = gameObject.GetComponent<AttachToController>();

    while (!attach.IsAttached) {
        yield return null;
    }

    // Perform setup here
}

第 3 章 - タッチパッド入力の操作

目標

  • タッチパッド入力データ イベントを取得する方法を学習する
  • アプリ エクスペリエンスにタッチパッド軸位置情報を使用する方法を学習する

Instructions

  • [階層] パネルで ColorPickerWheel をクリックします
  • [インスペクター] パネルの [アニメーター]ColorPickerWheelController をダブルクリックします
  • 開かれた [アニメーター] タブを確認できます

Unity のアニメーション コントローラーによる UI の表示/非表示

アニメーションを使用して ColorPickerWheel UI を表示または非表示にするために、Unity のアニメーション システムを使用します。 ColorPickerWheelVisible プロパティを true または false に設定すると、[表示] および [非表示] アニメーション トリガーが作動します。 [表示] および [非表示] パラメーターは、ColorPickerWheelController アニメーション コントローラーで定義されます。

Unity アニメーション コントローラー

手順

  • [階層] パネルで ColorPickerWheel プレハブを選択します
  • [インスペクター] パネルで ColorPickerWheel スクリプトをダブルクリックして、コードを Visual Studio で表示します

ColorPickerWheel スクリプト

ColorPickerWheel は、Unity の InteractionSourceUpdated イベントにサブスクライブしてタッチパッド イベントをリッスンします。

InteractionSourceUpdated() で、スクリプトは最初に次のことを確認します。

  • 実際にタッチパッド イベント (obj. state.touchpadTouched) である
  • 左のコントローラー (obj.state.source.handedness) が起点である

両方とも true の場合は、タッチパッドの位置 (obj.touchpadPosition) が selectorPosition に割り当てられます。

private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
    if (obj.state.source.handedness == handedness && obj.state.touchpadTouched)
    {
        Visible = true;
        selectorPosition = obj.state.touchpadPosition;
    }
}

Update() では、visible プロパティに基づいて、カラー ピッカーのアニメーター コンポーネントで、[表示] および [非表示] アニメーション トリガーが作動します

if (visible != visibleLastFrame)
{
    if (visible)
    {
        animator.SetTrigger("Show");
    }
    else
    {
        animator.SetTrigger("Hide");
    }
}

Update() では、selectorPosition を使用して、カラー ホイールのメッシュ コライダーでレイキャスティングを行います。これにより、UV 位置が返されます。 次に、この位置を使用して、カラー ホイールのテクスチャのピクセル座標と色の値を見つけることができます。 この値には、SelectedColor プロパティを使用して、他のスクリプトからアクセスできます。

カラー ピッカー ホイール レイキャスト

...
    // Clamp selector position to a radius of 1
    Vector3 localPosition = new Vector3(selectorPosition.x * inputScale, 0.15f, selectorPosition.y * inputScale);
    if (localPosition.magnitude > 1)
    {
        localPosition = localPosition.normalized;
    }
    selectorTransform.localPosition = localPosition;

    // Raycast the wheel mesh and get its UV coordinates
    Vector3 raycastStart = selectorTransform.position + selectorTransform.up * 0.15f;
    RaycastHit hit;
    Debug.DrawLine(raycastStart, raycastStart - (selectorTransform.up * 0.25f));

    if (Physics.Raycast(raycastStart, -selectorTransform.up, out hit, 0.25f, 1 << colorWheelObject.layer, QueryTriggerInteraction.Ignore))
    {
        // Get pixel from the color wheel texture using UV coordinates
        Vector2 uv = hit.textureCoord;
        int pixelX = Mathf.FloorToInt(colorWheelTexture.width * uv.x);
        int pixelY = Mathf.FloorToInt(colorWheelTexture.height * uv.y);
        selectedColor = colorWheelTexture.GetPixel(pixelX, pixelY);
        selectedColor.a = 1f;
    }
    // Set the selector's color and blend it with white to make it visible on top of the wheel
    selectorRenderer.material.color = Color.Lerp (selectedColor, Color.white, 0.5f);
}

第 4 章 - コントローラー モデルのオーバーライド

目標

  • カスタム 3D モデルを使用してコントローラー モデルをオーバーライドする方法を学習する。

MR213_BrushToolOverride

Instructions

  • [階層] パネルで MotionControllers をクリックします。
  • [代替右コントローラー] フィールドの右側にある円をクリックします。
  • BrushController」と入力し、結果からプレハブを選択します。 Assets/AppPrefabs/BrushController の下で見つけることもできます。
  • [常に代替右モデルを使用] をオンにします

MR213_BrushToolOverrideSlot

BrushController プレハブは、[階層] パネルに含まれている必要ありません。 ただし、子コンポーネントを確認するために、以下を実行します。

  • [プロジェクト] パネルで「BrushController」と入力し、BrushController プレハブを [階層] パネルにドラッグします。

MR213_BrushTool_Prefab2

BrushController[先端] コンポーネントを見つけます。 その変換を使用して、線の描画を開始/停止します。

  • [階層] パネルから BrushController を削除します。
  • シーンを保存し、[再生] ボタンをクリックします。 右側のモーション コントローラーがブラシ モデルに置き換えられていることがわかります。

第 5 章 - Select 入力による描画

目標

  • Select ボタン イベントを使用して線の描画を開始および停止する方法を学習する

Instructions

  • [プロジェクト] パネルで BrushController プレハブを検索します。
  • [インスペクター] パネルで BrushController スクリプトをダブルクリックして、コードを Visual Studio で表示します

BrushController スクリプト

BrushController は、InteractionManager の InteractionSourcePressed イベントと InteractionSourceReleased イベントにサブスクライブします。 InteractionSourcePressed イベントがトリガーされると、ブラシの Draw プロパティが true に設定され、InteractionSourceReleased イベントがトリガーされると、ブラシの Draw プロパティが false に設定されます。

private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
    if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
    {
        Draw = true;
    }
}

private void InteractionSourceReleased(InteractionSourceReleasedEventArgs obj)
{
    if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
    {
        Draw = false;
    }
}

Draw が true に設定されている間は、インスタンス化された Unity LineRenderer でブラシが点を生成します。 このプレハブへの参照は、ブラシの [Stroke プレハブ] フィールドに保持されます。

private IEnumerator DrawOverTime()
{
    // Get the position of the tip
    Vector3 lastPointPosition = tip.position;

    ...

    // Create a new brush stroke
    GameObject newStroke = Instantiate(strokePrefab);
    LineRenderer line = newStroke.GetComponent<LineRenderer>();
    newStroke.transform.position = startPosition;
    line.SetPosition(0, tip.position);
    float initialWidth = line.widthMultiplier;

    // Generate points in an instantiated Unity LineRenderer
    while (draw)
    {
        // Move the last point to the draw point position
        line.SetPosition(line.positionCount - 1, tip.position);
        line.material.color = colorPicker.SelectedColor;
        brushRenderer.material.color = colorPicker.SelectedColor;
        lastPointAddedTime = Time.unscaledTime;
        // Adjust the width between 1x and 2x width based on strength of trigger pull
        line.widthMultiplier = Mathf.Lerp(initialWidth, initialWidth * 2, width);

        if (Vector3.Distance(lastPointPosition, tip.position) > minPositionDelta || Time.unscaledTime > lastPointAddedTime + maxTimeDelta)
        {
            // Spawn a new point
            lastPointAddedTime = Time.unscaledTime;
            lastPointPosition = tip.position;
            line.positionCount += 1;
            line.SetPosition(line.positionCount - 1, lastPointPosition);
        }
        yield return null;
    }
}

カラー ピッカーのホイール UI から、現在選択されている色を使用するには、BrushController に、ColorPickerWheel オブジェクトへの参照がある必要があります。 BrushController プレハブは実行時に交換用コントローラーとしてインスタンス化されるため、シーン内のオブジェクトへの参照は、実行時に設定する必要があります。 この場合、GameObject.FindObjectOfType を使用して ColorPickerWheel を見つけます。

private void OnEnable()
{
    // Locate the ColorPickerWheel
    colorPicker = FindObjectOfType<ColorPickerWheel>();

    // Assign currently selected color to the brush’s material color
    brushRenderer.material.color = colorPicker.SelectedColor;
    ...
}
  • シーンを保存し、[再生] ボタンをクリックします。 右側のコントローラーの Select ボタンを使用して、線を描画し、塗りつぶすことができます。

第 6 章 - Select 入力によるオブジェクトの生成

目標

  • Select ボタンと Grasp ボタンの入力イベントを使用する方法を学習する
  • オブジェクトをインスタンス化する方法を学習する

Instructions

  • [プロジェクト] パネルで、検索ボックスに「ObjectSpawner」と入力します。 これは、Assets/AppPrefabs/ の下で見つけることもできます

  • ObjectSpawner プレハブを [階層] パネルにドラッグします。

  • [階層] パネルで ObjectSpawner をクリックします。

  • ObjectSpawner には、Color Source という名前のフィールドがあります。

  • [階層] パネルで、このフィールドに ColorPickerWheel 参照をドラッグします。

    オブジェクトスポナーインスペクター

  • [階層] パネルで ObjectSpawner プレハブをクリックします。

  • [インスペクター] パネルで ObjectSpawner スクリプトをダブルクリックして、コードを Visual Studio で表示します。

ObjectSpawner スクリプト

ObjectSpawner は、プリミティブ メッシュ (立方体、球、円柱) のコピーを空間にインスタンス化します。 InteractionSourcePressed が検出されると、利き手が確認され、またそれが InteractionSourcePressType.Grasp イベントと InteractionSourcePressType.Select イベントのどちらであるかが確認されます。

Grasp イベントの場合、現在のメッシュの種類 (球、立方体、円柱) のインデックスがインクリメントされます

private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
    // Check handedness, see if it is left controller
    if (obj.state.source.handedness == handedness)
    {
        switch (obj.pressType)
        {
            // If it is Select button event, spawn object
            case InteractionSourcePressType.Select:
                if (state == StateEnum.Idle)
                {
                    // We've pressed the grasp - enter spawning state
                    state = StateEnum.Spawning;
                    SpawnObject();
                }
                break;

            // If it is Grasp button event
            case InteractionSourcePressType.Grasp:

                // Increment the index of current mesh type (sphere, cube, cylinder)
                meshIndex++;
                if (meshIndex >= NumAvailableMeshes)
                {
                    meshIndex = 0;
                }
                break;

            default:
                break;
        }
    }
}

Select イベントの場合、SpawnObject() で、新しいオブジェクトがインスタンス化され、親の指定が解除され、ワールドにリリースされます。

private void SpawnObject()
{
    // Instantiate the spawned object
    GameObject newObject = Instantiate(displayObject.gameObject, spawnParent);
    // Detach the newly spawned object
    newObject.transform.parent = null;
    // Reset the scale transform to 1
    scaleParent.localScale = Vector3.one;
    // Set its material color so its material gets instantiated
    newObject.GetComponent<Renderer>().material.color = colorSource.SelectedColor;
}

ObjectSpawner は、ColorPickerWheel を使用して、表示オブジェクトの素材の色を設定します。 生成されたオブジェクトには、この素材のインスタンスが与えられ、その色が保持されます。

  • シーンを保存し、[再生] ボタンをクリックします。

[Grasp] ボタンを使用してオブジェクトを変更でき、また [Select] ボタンを使用してオブジェクトを生成できます。

アプリをビルドし、Mixed Reality ポータルにデプロイする

  • Unity で、[File] (ファイル) > [Build Settings] (ビルド設定) の順に選択します。
  • [開いているシーンを追加] をクリックして、現在のシーンを [ビルド内のシーン] に追加します。
  • [ビルド] をクリックします。
  • 「App」という名前の新しいフォルダーを作成します。
  • [App] フォルダーを 1 回クリックします。
  • [フォルダーの選択] をクリックします。
  • Unity での作業が終了すると、エクスプローラー ウィンドウが表示されます。
  • [App] フォルダーを開きます。
  • Visual Studio ソリューション ファイル、YourSceneName.sln をダブルクリックします。
  • Visual Studio の上部ツールバーを使用して、ターゲットを [デバッグ] から [リリース] に変更し、[ARM] から [X64] に変更します。
  • [デバイス] ボタンの横にあるドロップダウン矢印をクリックし、[ローカル コンピューター] を選択します。
  • メニューで [デバッグ] > [デバッグなしで開始] をクリックするか、Ctrl + F5 キーを押します。

これで、アプリがビルドされ、Mixed Reality ポータルにインストールされます。 これは、Mixed Reality ポータルの [スタート] メニューから再び起動できます。

高度なデザイン - 放射状レイアウトによるブラシ ツール

MixedReality213 Main

この章では、既定のモーション コントローラー モデルをカスタムのブラシ ツール コレクションに置き換える方法について説明します。 参照用として、完成したシーン MixedReality213Advanced[Scenes] フォルダーにあります。

Instructions

  • [プロジェクト] パネルで、検索ボックスに「BrushSelector」と入力します。 これは、Assets/AppPrefabs/ の下で見つけることもできます

  • BrushSelector プレハブを [階層] パネルにドラッグします。

  • 編成用に Brushes という名前の空の GameObject を作成します

  • 次のプレハブを [プロジェクト] パネルから Brushes にドラッグします

    • Assets/AppPrefabs/BrushFat
    • Assets/AppPrefabs/BrushThin
    • Assets/AppPrefabs/Eraser
    • Assets/AppPrefabs/MarkerFat
    • Assets/AppPrefabs/MarkerThin
    • Assets/AppPrefabs/Pencil

    ブラシ

  • [階層] パネルで MotionControllers プレハブをクリックします。

  • [インスペクター] パネルの [モーション コントローラー ビジュアライザー][常に代替右モデルを使用する] チェックボックスをオフにします

  • [階層] パネルで BrushSelector をクリックします

  • BrushSelector には、ColorPicker という名前のフィールドがあります

  • [階層] パネルの ColorPickerWheel を、[インスペクター] パネルの [ColorPicker] フィールドにドラッグします。

    ColorPickerWheel をブラシ セレクターに割り当てる

  • [階層] パネルの BrushSelector プレハブで Menu オブジェクトを選択します。

  • [インスペクター] パネルの LineObjectCollection コンポーネントで [Objects array] (オブジェクト配列) ドロップダウンを開きます。 6 個の空のスロットが表示されます。

  • [階層] パネルから、Brushes GameObject の下の各親プレハブを、これらのスロットに任意の順序でドラッグします。 (必ず、プロジェクト フォルダー内ではなく、シーンからプレハブをドラッグしてください。)

ブラシ セレクター

BrushSelector プレハブ

BrushSelectorAttachToController を継承するため、[利き手] オプションと [要素] オプションが [インスペクター] パネルに表示されます。 [右] および [ポインティング ポーズ] を選択して、右手のコントローラーにブラシ ツールを前向きにアタッチします。

BrushSelector は、2 つのユーティリティを使用します。

  • Ellipse: 楕円形に沿って、空間で点を生成するために使用されます。
  • LineObjectCollection: Line クラス (Ellipse など) によって生成された点を使用してオブジェクトを分布させます。 ここでは、楕円の形状に沿ってブラシを配置するために使用します。

これらのユーティリティを組み合わせて使用すると、放射状メニューを作成できます。

LineObjectCollection スクリプト

LineObjectCollection には、その線に沿って分布するオブジェクトのサイズ、位置、回転のコントロールがあります。 これは、ブラシ セレクターのような放射状メニューを作成する場合に便利です。 選択された中心位置に近づくにつれ、何も表示されていない状態からスケールアップするブラシの外観を作成するために、ObjectScale 曲線は中央を頂点として、両端が下がっています。

BrushSelector スクリプト

BrushSelector で、手続き型アニメーションの使用を選択しました。 最初に、ブラシ モデルが LineObjectCollection スクリプトによって楕円内に分配されます。 次に、各ブラシによって、DisplayMode 値に基づいて、ユーザーの手の中でその位置が維持されます。この値は、選択内容に基づいて変化します。 手続き型の手法を選択したのは、ユーザーがブラシを選択したときに、ブラシの位置の切り替えが中断される可能性が高いためです。 Mecanim アニメーションは中断を適切に処理できますが、単純な Lerp 操作よりも複雑である傾向があります。

BrushSelector は、両方の組み合わせを使用します。 タッチパッド入力が検出されると、各ブラシ オプションが表示され、放射状メニューに沿ってスケール アップされます。 タイムアウト期間が経過する (ユーザーが選択を行ったことを示す) と、各ブラシ オプションが再びスケールダウンされ、選択したブラシだけが残ります。

タッチパッド入力の視覚化

コントローラー モデルが完全に置き換えられている場合でも、元のモデル入力で入力を表示すると便利な場合があります。 これは、実際のユーザーの操作に安心感を与えるために役立ちます。 BrushSelector で、入力を受け取ったときにタッチパッドが短時間表示されることを選択しました。 これを行うために、コントローラーから Touchpad 要素を取得し、その素材をカスタム素材に置き換えてから、最後に受け取ったタッチパッド入力に基づいて、その素材の色にグラデーションを適用しました。

protected override void OnAttachToController()
{
    // Turn off the default controller's renderers
    controller.SetRenderersVisible(false);

    // Get the touchpad and assign our custom material to it
    Transform touchpad;
    if (controller.TryGetElement(MotionControllerInfo.ControllerElementEnum.Touchpad, out touchpad))
    {
        touchpadRenderer = touchpad.GetComponentInChildren<MeshRenderer>();
        originalTouchpadMaterial = touchpadRenderer.material;
        touchpadRenderer.material = touchpadMaterial;
        touchpadRenderer.enabled = true;
    }

    // Subscribe to input now that we're parented under the controller
    InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
}

private void Update()
{
    ...
    // Update our touchpad material
    Color glowColor = touchpadColor.Evaluate((Time.unscaledTime - touchpadTouchTime) / touchpadGlowLossTime);
    touchpadMaterial.SetColor("_EmissionColor", glowColor);
    touchpadMaterial.SetColor("_Color", glowColor);
    ...
}

タッチパッド入力によるブラシ ツールの選択

ブラシ セレクターは、タッチパッドを押すことによる入力をが検出すると、入力の位置を調べて、左と右のどちらであるかを判別します。

selectPressedAmount によるストロークの太さ

InteractionSourcePressed()InteractionSourcePressType.Select イベントを取得する代わりに、selectPressedAmount を通じて、押された量のアナログ値を取得できます。 この値は InteractionSourceUpdated() で取得できます。

private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
    if (obj.state.source.handedness == handedness)
    {
        if (obj.state.touchpadPressed)
        {
            // Check which side we clicked
            if (obj.state.touchpadPosition.x < 0)
            {
                currentAction = SwipeEnum.Left;
            }
            else
            {
                currentAction = SwipeEnum.Right;
            }

            // Ping the touchpad material so it gets bright
            touchpadTouchTime = Time.unscaledTime;
        }

        if (activeBrush != null)
        {
            // If the pressed amount is greater than our threshold, draw
            if (obj.state.selectPressedAmount >= selectPressedDrawThreshold)
            {
                activeBrush.Draw = true;
                activeBrush.Width = ProcessSelectPressedAmount(obj.state.selectPressedAmount);
            }
            else
            {
                // Otherwise, stop drawing
                activeBrush.Draw = false;
                selectPressedSmooth = 0f;
            }
        }
    }
}

Eraser スクリプト

Eraser は、基本 BrushDrawOverTime() 関数をオーバーライドする特殊な種類のブラシです。 Draw が true の場合、Eraser は、その先端が既存のブラシ ストロークと交差しているかどうかを調べます。 これに該当する場合、それらはキューに追加され、縮小され削除されます。

高度な設計 - テレポートと移動

ユーザーがサムスティックを使用してテレポートでシーン内を移動できるようにする場合は、MixedRealityCamera ではなく、MixedRealityCameraParent を使用します。 InputManagerDefaultCursor を追加することも必要です。 MixedRealityCameraParent には、既に MotionControllersBoundary が子コンポーネントとして含まれているため、既存の MotionControllers プレハブと Environment プレハブを削除する必要があります。

Instructions

  • [階層] パネルで、MixedRealityCameraEnvironmentMotionControllers を削除します

  • [プロジェクト] パネルで、次のプレハブを検索し、[階層] パネルにドラッグします。

    • Assets/AppPrefabs/Input/Prefabs/MixedRealityCameraParent
    • Assets/AppPrefabs/Input/Prefabs/InputManager
    • Assets/AppPrefabs/Input/Prefabs/Cursor/DefaultCursor

    Mixed Reality カメラの親

  • [階層] パネルで Input Manager をクリックします

  • [インスペクター] パネルで、[シンプル シングル ポインター セレクター] セクションまで下にスクロールします

  • [階層] パネルから DefaultCursor[Cursor] フィールドにドラッグします

    DefaultCursor の割り当て

  • シーンを保存し、[再生] ボタンをクリックします。 サムスティックを使用して、左/右に回転したり、テレポートしたりできるようになります。

終わりに

これで、このチュートリアルは終了です。 学習した内容は次のとおりです。

  • Unity のゲーム モードとランタイムでモーション コントローラー モデルを操作する方法。
  • さまざまな種類のボタン イベントを使用する方法とその応用。
  • コントローラー上に UI 要素を重ね合わせる方法や、UI 要素を完全にカスタマイズする方法。

これで、モーション コントローラーを使用して、独自のイマーシブ エクスペリエンスを作成できるようになりました。

完成したシーン

  • Unity の[プロジェクト] パネルで [Scenes] フォルダーをクリックします。
  • 2 つの Unity シーン、MixedReality213MixedReality213Advanced が見つかります。
    • MixedReality213: 1 つのブラシを使用した完成済みシーン
    • MixedReality213Advanced: 選択ボタンの押す量の例を含む複数のブラシがある完成済みシーン

関連項目